├── .idea
├── .name
├── codeStyles
│ └── codeStyleConfig.xml
├── compiler.xml
├── vcs.xml
├── render.experimental.xml
├── deploymentTargetDropDown.xml
└── gradle.xml
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── font
│ │ │ │ ├── work_sans.ttf
│ │ │ │ ├── work_sans_bold.ttf
│ │ │ │ ├── work_sans_thin.ttf
│ │ │ │ ├── work_sans_black.ttf
│ │ │ │ ├── work_sans_light.ttf
│ │ │ │ ├── work_sans_medium.ttf
│ │ │ │ ├── work_sans_semi_bold.ttf
│ │ │ │ ├── work_sans_extra_bold.ttf
│ │ │ │ ├── work_sans_extra_light.ttf
│ │ │ │ └── work_sans_font_family.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── drawable
│ │ │ │ ├── apk_file_icon.xml
│ │ │ │ ├── check_box_selector.xml
│ │ │ │ ├── slide_down_indicator.xml
│ │ │ │ ├── home_icon.xml
│ │ │ │ ├── arrow_down.xml
│ │ │ │ ├── ic_baseline_check_circle_24.xml
│ │ │ │ ├── close_icon.xml
│ │ │ │ ├── send_icon.xml
│ │ │ │ ├── close_icon_24.xml
│ │ │ │ ├── ic_baseline_radio_button_unchecked_24.xml
│ │ │ │ ├── person_icon.xml
│ │ │ │ ├── ic_baseline_play_circle_outline_24.xml
│ │ │ │ ├── ic_baseline_arrow_circle_down_24.xml
│ │ │ │ ├── navigate_next.xml
│ │ │ │ ├── ic_baseline_folder_open_24.xml
│ │ │ │ ├── ic_baseline_folder_open_24_two.xml
│ │ │ │ ├── pad_lock_icon.xml
│ │ │ │ ├── history_icon.xml
│ │ │ │ ├── share_icon.xml
│ │ │ │ ├── video_icon.xml
│ │ │ │ ├── plain_file_icon.xml
│ │ │ │ ├── globe_icon.xml
│ │ │ │ ├── pdf.xml
│ │ │ │ ├── ic_dat_file_logo.xml
│ │ │ │ ├── text_file_icon.xml
│ │ │ │ └── multi_colored_apple_icon.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values-night
│ │ │ │ └── colors.xml
│ │ │ ├── values
│ │ │ │ ├── attrs.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── colors.xml
│ │ │ ├── layout
│ │ │ │ ├── category_chip.xml
│ │ │ │ ├── no_item_in_transfer_or_receive_layout.xml
│ │ │ │ ├── fragment_break_connection.xml
│ │ │ │ ├── fragment_audio.xml
│ │ │ │ ├── fragment_app.xml
│ │ │ │ ├── fragment_videos.xml
│ │ │ │ ├── image_layout_item.xml
│ │ │ │ ├── fragment_sent_data.xml
│ │ │ │ ├── all_media_on_device.xml
│ │ │ │ ├── image_transfer_layout_item.xml
│ │ │ │ ├── fragment_received_data.xml
│ │ │ │ ├── connected_to_peer_transfer_ongoing_persistent_bottom_sheet.xml
│ │ │ │ ├── media_date_modified_header.xml
│ │ │ │ ├── expanded_connected_to_peer_transfer_ongoing.xml
│ │ │ │ ├── collapsed_connected_to_peer_transfer_receive_ongoing_layout.xml
│ │ │ │ ├── fragment_files.xml
│ │ │ │ ├── discovered_peer_layout_item.xml
│ │ │ │ ├── zip_bolt_header_layout.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── folder_layout_item.xml
│ │ │ │ ├── fragment_image.xml
│ │ │ │ ├── expanded_bottom_sheet_layout_toolbar.xml
│ │ │ │ ├── wifi_p2p_device_item_layout.xml
│ │ │ │ ├── home_screen_recyclerview_layout_item.xml
│ │ │ │ ├── application_layout_item.xml
│ │ │ │ └── zip_bolt_send_file_header_layout.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── values-v23
│ │ │ │ └── themes.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── salesground
│ │ │ │ └── zipbolt
│ │ │ │ ├── ZipBoltApplication.kt
│ │ │ │ ├── ui
│ │ │ │ ├── recyclerview
│ │ │ │ │ ├── RecyclerViewItemClickedListener.kt
│ │ │ │ │ ├── DataToTransferRecyclerViewDiffUtil.kt
│ │ │ │ │ ├── SentAndReceivedDataItemsViewPagerAdapter.kt
│ │ │ │ │ ├── videoFragment
│ │ │ │ │ │ └── VideoFragmentRecyclerViewAdapter.kt
│ │ │ │ │ ├── audioFragment
│ │ │ │ │ │ └── AudioFragmentRecyclerViewAdapter.kt
│ │ │ │ │ ├── sentDataFragment
│ │ │ │ │ │ └── viewHolders
│ │ │ │ │ │ │ ├── image
│ │ │ │ │ │ │ ├── ImageTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── ImageTransferCompleteLayoutViewHolder.kt
│ │ │ │ │ │ │ ├── plainFile
│ │ │ │ │ │ │ ├── PlainFileTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── image
│ │ │ │ │ │ │ │ ├── PlainImageFileTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ │ └── PlainImageFileTransferCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── video
│ │ │ │ │ │ │ │ ├── PlainVideoFileTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ │ └── PlainVideoFileTransferCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── document
│ │ │ │ │ │ │ │ ├── PlainDocumentFileTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ │ └── PlainDocumentFileTransferCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── PlainFileTransferCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── directory
│ │ │ │ │ │ │ ├── DirectoryTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── DirectoryTransferCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── audio
│ │ │ │ │ │ │ ├── AudioTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── AudioTransferCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── video
│ │ │ │ │ │ │ ├── VideoTransferWaitingLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── VideoTransferCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── application
│ │ │ │ │ │ │ ├── ApplicationTransferWaitingViewHolder.kt
│ │ │ │ │ │ │ └── ApplicationTransferCompleteLayoutViewHolder.kt
│ │ │ │ │ ├── imagefragment
│ │ │ │ │ │ └── DateModifiedHeaderViewHolder.kt
│ │ │ │ │ ├── applicationFragment
│ │ │ │ │ │ └── ApplicationFragmentAppsDisplayRecyclerViewAdapter.kt
│ │ │ │ │ ├── receivedDataFragment
│ │ │ │ │ │ └── viewHolders
│ │ │ │ │ │ │ ├── image
│ │ │ │ │ │ │ └── ImageReceiveCompleteLayoutViewHolder.kt
│ │ │ │ │ │ │ ├── plainFile
│ │ │ │ │ │ │ ├── document
│ │ │ │ │ │ │ │ └── PlainDocumentFileReceiveCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── image
│ │ │ │ │ │ │ │ └── PlainImageFileReceiveCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── video
│ │ │ │ │ │ │ │ └── PlainVideoFileReceiveCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── directory
│ │ │ │ │ │ │ └── DirectoryReceiveCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── audio
│ │ │ │ │ │ │ └── AudioReceiveCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ ├── video
│ │ │ │ │ │ │ └── VideoReceiveCompleteLayoutItemViewHolder.kt
│ │ │ │ │ │ │ └── application
│ │ │ │ │ │ │ └── ApplicationReceiveCompleteLayoutViewHolder.kt
│ │ │ │ │ └── peersDiscoveryFragment
│ │ │ │ │ │ ├── PeersDiscoveredRecyclerViewAdapter.kt
│ │ │ │ │ │ └── DiscoveredPeerLayoutItemViewHolder.kt
│ │ │ │ ├── fragments
│ │ │ │ │ └── BreakConnectionFragment.kt
│ │ │ │ ├── customviews
│ │ │ │ │ ├── ConstraintLayoutWithTopBorderLine.kt
│ │ │ │ │ ├── SelectableLinearLayout.kt
│ │ │ │ │ ├── AnimatedLoadingTextView.kt
│ │ │ │ │ ├── DividerLabel.kt
│ │ │ │ │ └── SelectableConstraintLayout.kt
│ │ │ │ ├── AllMediaOnDeviceViewPager2Adapter.kt
│ │ │ │ └── animationutils
│ │ │ │ │ └── AnimationUtils.kt
│ │ │ │ ├── repository
│ │ │ │ ├── FileRepository.kt
│ │ │ │ ├── SavedFilesRepository.kt
│ │ │ │ ├── AudioRepository.kt
│ │ │ │ ├── VideoRepositoryI.kt
│ │ │ │ ├── ApplicationsRepositoryInterface.kt
│ │ │ │ ├── ImageRepository.kt
│ │ │ │ └── implementation
│ │ │ │ │ ├── ZipBoltFileRepository.kt
│ │ │ │ │ └── ZipBoltSavedFilesRepository.kt
│ │ │ │ ├── PermissionUtils.kt
│ │ │ │ ├── model
│ │ │ │ ├── ui
│ │ │ │ │ ├── PeerConnectionUIState.kt
│ │ │ │ │ └── ImagesDisplayModel.kt
│ │ │ │ ├── MediaModel.kt
│ │ │ │ └── MediaType.kt
│ │ │ │ ├── MainActivityDataBindingUtils.kt
│ │ │ │ ├── di
│ │ │ │ ├── CommunicationDIModule.kt
│ │ │ │ ├── RepositoryDIModule.kt
│ │ │ │ └── AndroidComponentsDIModule.kt
│ │ │ │ ├── utils
│ │ │ │ ├── SingleLiveDataEventForUIState.kt
│ │ │ │ └── TimeUtils.kt
│ │ │ │ ├── viewmodel
│ │ │ │ ├── GeneralViewModel.kt
│ │ │ │ ├── AudioViewModel.kt
│ │ │ │ ├── VideoViewModel.kt
│ │ │ │ ├── ApplicationsViewModel.kt
│ │ │ │ ├── PeersDiscoveryViewModel.kt
│ │ │ │ ├── MainActivityViewModel.kt
│ │ │ │ ├── DataToTransferViewModel.kt
│ │ │ │ └── ReceivedDataViewModel.kt
│ │ │ │ ├── broadcast
│ │ │ │ ├── SendDataBroadcastReceiver.kt
│ │ │ │ └── DataTransferServiceConnectionStateReceiver.kt
│ │ │ │ └── communication
│ │ │ │ ├── MediaTransferProtocol.kt
│ │ │ │ └── ZipBoltMTP.kt
│ │ └── AndroidManifest.xml
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── salesground
│ │ │ └── zipbolt
│ │ │ ├── HiltTestRunner.kt
│ │ │ ├── di
│ │ │ └── TestCommunicationDIModule.kt
│ │ │ └── repository
│ │ │ ├── ZipBoltFileRepositoryTest.kt
│ │ │ ├── ZipBoltSavedFilesRepositoryTest.kt
│ │ │ ├── DeviceApplicationsRepositoryTest.kt
│ │ │ └── ZipBoltImageRepositoryTest.kt
│ └── test
│ │ └── java
│ │ └── com
│ │ └── salesground
│ │ └── zipbolt
│ │ ├── utils
│ │ └── FileExtensionsKtTest.kt
│ │ ├── TestCoroutineRule.kt
│ │ ├── TimeUtilsTest.kt
│ │ └── LiveDataTestUtils.kt
└── proguard-rules.pro
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── settings.gradle
├── .gitignore
├── gradle.properties
└── gradlew.bat
/.idea/.name:
--------------------------------------------------------------------------------
1 | SpeedForce
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans.ttf
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_thin.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_black.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_light.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_medium.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_semi_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_semi_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_extra_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_extra_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_extra_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/font/work_sans_extra_light.ttf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pekwerike/ZipBolt/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/render.experimental.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ZipBoltApplication.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class ZipBoltApplication : Application() {
8 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 | }
8 | rootProject.name = "SpeedForce"
9 | include ':app'
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jun 20 22:36:34 WAT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/apk_file_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF121212
4 | #FFFFFFFF
5 | #616161
6 | #FF121212
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/check_box_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/RecyclerViewItemClickedListener.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview
2 |
3 |
4 | class RecyclerViewItemClickedListener(
5 | private val clicked: (T) -> Unit
6 | ) {
7 | fun onClick(clickedItem: T) {
8 | return clicked(clickedItem)
9 | }
10 | }
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/slide_down_indicator.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/home_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_down.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_check_circle_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/FileRepository.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import com.salesground.zipbolt.communication.MediaTransferProtocol
4 | import com.salesground.zipbolt.model.DataToTransfer
5 | import java.io.DataInputStream
6 | import java.io.File
7 |
8 | interface FileRepository {
9 | suspend fun getRootDirectory(): File
10 | suspend fun getDirectoryChildren(directoryPath: String): List
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/close_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/send_icon.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/close_icon_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_radio_button_unchecked_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/person_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_play_circle_outline_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/PermissionUtils.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt
2 |
3 | import android.Manifest
4 | import android.app.Activity
5 | import android.content.pm.PackageManager
6 | import androidx.core.app.ActivityCompat
7 | import androidx.lifecycle.Lifecycle
8 | import androidx.lifecycle.LifecycleObserver
9 | import androidx.lifecycle.OnLifecycleEvent
10 |
11 |
12 |
13 | object PermissionUtils : LifecycleObserver {
14 |
15 | const val READ_WRITE_STORAGE_REQUEST_CODE = 101
16 |
17 |
18 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_arrow_circle_down_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/navigate_next.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_folder_open_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_folder_open_24_two.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/salesground/zipbolt/HiltTestRunner.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import androidx.test.runner.AndroidJUnitRunner
6 | import dagger.hilt.android.testing.HiltTestApplication
7 |
8 | class HiltTestRunner : AndroidJUnitRunner(){
9 |
10 |
11 | override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
12 | return super.newApplication(cl, HiltTestApplication::class.java.name, context)
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/category_chip.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/no_item_in_transfer_or_receive_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_break_connection.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pad_lock_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/history_icon.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/salesground/zipbolt/utils/FileExtensionsKtTest.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.utils
2 |
3 | import androidx.core.math.MathUtils
4 | import org.junit.Assert.*
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 | import org.junit.runners.JUnit4
9 | import java.math.MathContext
10 |
11 | @RunWith(JUnit4::class)
12 | class FileExtensionsKtTest {
13 |
14 | @Test
15 | fun transformDataSizeToMeasuredUnit() {
16 | assertEquals("1.2mb", 1200000L.transformDataSizeToMeasuredUnit())
17 | assertEquals("1.3mb", 1290000L.transformDataSizeToMeasuredUnit())
18 | assertEquals("13.1mb", 13091000L.transformDataSizeToMeasuredUnit())
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/model/ui/PeerConnectionUIState.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.model.ui
2 |
3 | import android.net.wifi.p2p.WifiP2pDevice
4 | import android.net.wifi.p2p.WifiP2pInfo
5 | import com.salesground.zipbolt.model.DataToTransfer
6 |
7 | /*
8 | "Created PeerConnectionUIState to represent the various states of the
9 | persistent bottom sheet layout during peer discovery and connection"
10 | */
11 | sealed class PeerConnectionUIState {
12 |
13 | object NoConnectionUIAction : PeerConnectionUIState()
14 |
15 | object CollapsedConnectedToPeerTransferOngoing : PeerConnectionUIState()
16 |
17 | object ExpandedConnectedToPeerTransferOngoing : PeerConnectionUIState()
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/MainActivityDataBindingUtils.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt
2 |
3 | import android.app.Activity
4 | import android.view.ViewStub
5 | import com.salesground.zipbolt.databinding.*
6 |
7 |
8 | object MainActivityDataBindingUtils {
9 |
10 | fun getConnectedToPeerTransferOngoingPersistentBottomSheetBinding(activity: Activity):
11 | ConnectedToPeerTransferOngoingPersistentBottomSheetBinding {
12 | val view =
13 | activity.findViewById(R.id.connected_to_peer_transfer_ongoing_persistent_bottom_sheet_view_stub)
14 | .inflate()
15 | return ConnectedToPeerTransferOngoingPersistentBottomSheetBinding.bind(view)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/DataToTransferRecyclerViewDiffUtil.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 | import com.salesground.zipbolt.model.DataToTransfer
5 |
6 | class DataToTransferRecyclerViewDiffUtil : DiffUtil.ItemCallback() {
7 | override fun areItemsTheSame(
8 | oldItem: DataToTransfer,
9 | newItem: DataToTransfer
10 | ): Boolean {
11 | return oldItem.dataUri == newItem.dataUri
12 | }
13 |
14 | override fun areContentsTheSame(
15 | oldItem: DataToTransfer,
16 | newItem: DataToTransfer
17 | ): Boolean {
18 | return oldItem == newItem
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_audio.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/di/CommunicationDIModule.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.di
2 |
3 | import com.salesground.zipbolt.communication.MediaTransferProtocol
4 | import com.salesground.zipbolt.communication.implementation.MediaTransferProtocolImpl
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.android.components.ServiceComponent
9 | import dagger.hilt.components.SingletonComponent
10 |
11 |
12 | @InstallIn(ServiceComponent::class)
13 | @Module
14 | abstract class
15 | CommunicationDIModule {
16 |
17 | @Binds
18 | abstract fun getMediaTransferProtocol(
19 | mediaTransferProtocolImpl:
20 | MediaTransferProtocolImpl
21 | ): MediaTransferProtocol
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/SavedFilesRepository.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import java.io.File
4 |
5 | interface SavedFilesRepository {
6 |
7 | enum class ZipBoltMediaCategory(val categoryName: String) {
8 | IMAGES_BASE_DIRECTORY("ZipBolt Images"),
9 | VIDEOS_BASE_DIRECTORY("ZipBolt Videos"),
10 | AUDIO_BASE_DIRECTORY("ZipBolt Audios"),
11 | FILES_BASE_DIRECTORY("Files"),
12 | APPS_BASE_DIRECTORY("Apps"),
13 | FOLDERS_BASE_DIRECTORY("Folders"),
14 | DOCUMENTS_BASE_DIRECTORY("ZipBolt Documents")
15 | }
16 |
17 | fun getZipBoltBaseDirectory(): File
18 | fun getZipBoltMediaCategoryBaseDirectory(categoryType: ZipBoltMediaCategory): File
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/AudioRepository.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import com.salesground.zipbolt.communication.MediaTransferProtocol
4 | import com.salesground.zipbolt.model.DataToTransfer
5 | import java.io.DataInputStream
6 |
7 | interface AudioRepository {
8 |
9 | suspend fun insertAudioIntoMediaStore(
10 | audioName: String,
11 | audioSize: Long,
12 | audioDuration: Long,
13 | dataInputStream: DataInputStream,
14 | transferMetaDataUpdateListener: MediaTransferProtocol.TransferMetaDataUpdateListener,
15 | dataReceiveListener: MediaTransferProtocol.DataReceiveListener
16 | )
17 |
18 | suspend fun getAudioOnDevice(): MutableList
19 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_app.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/share_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/salesground/zipbolt/di/TestCommunicationDIModule.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.di
2 |
3 | import com.salesground.zipbolt.communication.MediaTransferProtocol
4 | import com.salesground.zipbolt.communication.implementation.MediaTransferProtocolImpl
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.hilt.components.SingletonComponent
8 | import dagger.hilt.testing.TestInstallIn
9 |
10 | @Module
11 | @TestInstallIn(
12 | components = [SingletonComponent::class],
13 | replaces = [CommunicationDIModule::class]
14 | )
15 | abstract class TestCommunicationDIModule {
16 |
17 | @Binds
18 | abstract fun getMediaTransferProtocol(
19 | mediaTransferProtocolImpl: MediaTransferProtocolImpl
20 | ): MediaTransferProtocol
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/model/MediaModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.model
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.drawable.Drawable
5 | import android.net.Uri
6 |
7 | enum class MediaCategory(private val mediaType: String) {
8 | IMAGE("image"),
9 | VIDEO("video"),
10 | AUDIO("audio")
11 | }
12 |
13 | data class MediaModel(
14 | val mediaUri: Uri,
15 | val mediaDisplayName: String?,
16 | val mediaDateAdded: Long,
17 | val mediaSize: Long,
18 | val mediaCategory: MediaCategory,
19 | val mimeType: String,
20 | val mediaBucketName: String,
21 | val mediaDuration: Long = 0,
22 | val mediaAlbumArtPath: String = "",
23 | val mediaTitle: String = "",
24 | val mediaArtist: String = ""
25 | )
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_videos.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/VideoRepositoryI.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import android.net.Uri
4 | import com.salesground.zipbolt.communication.MediaTransferProtocol
5 | import com.salesground.zipbolt.model.DataToTransfer
6 | import java.io.DataInputStream
7 |
8 | interface VideoRepositoryI {
9 |
10 | suspend fun insertVideoIntoMediaStore(
11 | videoName: String,
12 | videoSize: Long,
13 | videoDuration: Long,
14 | dataInputStream: DataInputStream,
15 | transferMetaDataUpdateListener: MediaTransferProtocol.TransferMetaDataUpdateListener,
16 | dataReceiveListener: MediaTransferProtocol.DataReceiveListener
17 | )
18 |
19 | suspend fun getVideosOnDevice(): MutableList
20 |
21 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/image_layout_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/fragments/BreakConnectionFragment.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.fragments
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import com.salesground.zipbolt.R
9 |
10 |
11 | class BreakConnectionFragment : Fragment() {
12 |
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 |
17 | }
18 |
19 | override fun onCreateView(
20 | inflater: LayoutInflater, container: ViewGroup?,
21 | savedInstanceState: Bundle?
22 | ): View? {
23 | // Inflate the layout for this fragment
24 | return inflater.inflate(R.layout.fragment_break_connection, container, false)
25 | }
26 |
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_sent_data.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/ApplicationsRepositoryInterface.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import android.content.pm.ApplicationInfo
4 | import com.salesground.zipbolt.communication.MediaTransferProtocol
5 | import com.salesground.zipbolt.model.DataToTransfer
6 | import java.io.DataInputStream
7 |
8 | interface ApplicationsRepositoryInterface {
9 | suspend fun getAllAppsOnDevice(): MutableList
10 | suspend fun getNonSystemAppsOnDevice(): List
11 | suspend fun insertApplicationIntoDevice(
12 | appFileName: String,
13 | appSize: Long,
14 | dataInputStream: DataInputStream,
15 | transferMetaDataUpdateListener: MediaTransferProtocol.TransferMetaDataUpdateListener,
16 | dataReceiveListener: MediaTransferProtocol.DataReceiveListener
17 | )
18 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/all_media_on_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/image_transfer_layout_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_received_data.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/salesground/zipbolt/repository/ZipBoltFileRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import com.salesground.zipbolt.repository.implementation.ZipBoltFileRepository
4 | import kotlinx.coroutines.runBlocking
5 | import org.junit.Assert.*
6 |
7 | import org.junit.After
8 | import org.junit.Before
9 | import org.junit.Test
10 |
11 | class ZipBoltFileRepositoryTest {
12 |
13 | private lateinit var zipBoltFileRepository: FileRepository
14 |
15 | @Before
16 | fun setUp() {
17 | zipBoltFileRepository = ZipBoltFileRepository()
18 | }
19 |
20 | @After
21 | fun tearDown() {
22 | }
23 |
24 | @Test
25 | fun getRootDirectory() {
26 | }
27 |
28 | @Test
29 | fun getDirectoryChildren() {
30 | }
31 |
32 | @Test
33 | fun insertDirectory() {
34 | }
35 |
36 | @Test
37 | fun insertFile() {
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/utils/SingleLiveDataEventForUIState.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.utils
2 |
3 | import androidx.lifecycle.Observer
4 |
5 |
6 | open class SingleLiveDataEventForUIState(private val content: T) {
7 | // list of all observers that have received the send button event
8 | private val mutableSet = mutableSetOf()
9 |
10 | fun getEvent(fragmentName: String): T? {
11 | return if (mutableSet.contains(fragmentName)) {
12 | null
13 | } else {
14 | mutableSet.add(fragmentName)
15 | content
16 | }
17 | }
18 |
19 | }
20 |
21 | open class Event(private val content: T) {
22 | var isHandled = false
23 | fun getEventIfNotHandled(): T? {
24 | return if (!isHandled) {
25 | null
26 | } else {
27 | isHandled = true
28 | content
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/model/ui/ImagesDisplayModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.model.ui
2 |
3 | import com.salesground.zipbolt.model.DataToTransfer
4 |
5 | sealed class ImagesDisplayModel(val id: String) {
6 |
7 | override fun equals(other: Any?): Boolean {
8 | return when (other) {
9 | this -> true
10 | !is ImagesDisplayModel -> false
11 | else -> {
12 | other.id == this.id
13 | }
14 | }
15 | }
16 |
17 | override fun hashCode(): Int {
18 | val prime = 5
19 | val result = 7
20 | return prime * result + id.hashCode()
21 | }
22 |
23 | data class ImagesDateModifiedHeader(val dateModified: String) : ImagesDisplayModel(dateModified)
24 |
25 | data class DeviceImageDisplay(val deviceImage: DataToTransfer.DeviceImage) :
26 | ImagesDisplayModel(deviceImage.imageUri.toString())
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/video_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/connected_to_peer_transfer_ongoing_persistent_bottom_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/GeneralViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import com.salesground.zipbolt.utils.Event
7 | import com.salesground.zipbolt.utils.SingleLiveDataEventForUIState
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import javax.inject.Inject
10 |
11 |
12 | @HiltViewModel
13 | class GeneralViewModel @Inject constructor() : ViewModel() {
14 |
15 | private val _hasPermissionToFetchMedia = MutableLiveData>()
16 | val hasPermissionToFetchMedia : LiveData>
17 | get() = _hasPermissionToFetchMedia
18 |
19 | fun hasPermissionToFetchMedia(hasPermission: Boolean){
20 | _hasPermissionToFetchMedia.value = SingleLiveDataEventForUIState(hasPermission)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/broadcast/SendDataBroadcastReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.broadcast
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 |
7 | class SendDataBroadcastReceiver(
8 | private val sendDataButtonClickedListener: SendDataButtonClickedListener
9 | ): BroadcastReceiver() {
10 |
11 | companion object{
12 | const val ACTION_SEND_DATA_BUTTON_CLICKED = "ActionSendDataButtonClicked"
13 | }
14 |
15 | interface SendDataButtonClickedListener{
16 | fun sendDataButtonClicked()
17 | }
18 |
19 | override fun onReceive(context: Context?, intent: Intent?) {
20 | intent?.let{
21 | when(intent.action) {
22 | ACTION_SEND_DATA_BUTTON_CLICKED -> {
23 | sendDataButtonClickedListener.sendDataButtonClicked()
24 | }
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 53dp
4 | 12dp
5 | 16dp
6 | 4dp
7 | 8dp
8 | 4dp
9 | 48dp
10 | 8dp
11 | 4dp
12 | 2dp
13 | 4dp
14 | 8dp
15 | 8dp
16 | 4dp
17 | 16dp
18 | 8dp
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/media_date_modified_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
15 |
16 |
17 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/model/MediaType.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.model
2 |
3 | sealed class MediaType(val value: Int) {
4 | object Image : MediaType(1)
5 | object Video : MediaType(2)
6 | object Audio : MediaType(3)
7 | object App : MediaType(4)
8 |
9 | sealed class File(fileType: Int) : MediaType(fileType) {
10 | object ImageFile : File(5)
11 | object VideoFile : File(6)
12 | object AudioFile : File(7)
13 | object AppFile : File(8)
14 | object Directory : File(9)
15 |
16 | sealed class Document(documentType: Int) : File(documentType) {
17 | object PdfDocument : Document(10)
18 | object WordDocument : Document(11)
19 | object ExcelDocument : Document(12)
20 | object UnknownDocument : Document(13)
21 | object PowerPointDocument : Document(14)
22 | object ZipDocument : Document(15)
23 | object WebpageDocument : Document(16)
24 | object TextFileDocument: Document(17)
25 | object DatDocument: Document(18)
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/ImageRepository.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import com.salesground.zipbolt.communication.MediaTransferProtocol.*
4 | import com.salesground.zipbolt.model.DataToTransfer
5 | import java.io.DataInputStream
6 |
7 | /**
8 | * Functions description
9 | * A. insertImageIntoMediaStore
10 | * B. getMetaDataOfImage -> This function fetches the following details of an image Uri
11 | * 1. image mimeType
12 | * 2. image size
13 | * 3. image display name
14 | * These details above will be used for socket communication when transferring the image
15 | * C. getAllImagesOnDevice
16 | * D. getTenImagesOnDevice
17 | *
18 | */
19 | interface ImageRepository {
20 |
21 | suspend fun insertImageIntoMediaStore(
22 | displayName: String,
23 | size: Long,
24 | dataInputStream: DataInputStream,
25 | transferMetaDataUpdateListener: TransferMetaDataUpdateListener,
26 | dataReceiveListener: DataReceiveListener
27 | )
28 |
29 | suspend fun getImagesOnDevice(limit: Int = 0): MutableList
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/test/java/com/salesground/zipbolt/TestCoroutineRule.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.test.*
6 | import org.junit.rules.TestRule
7 | import org.junit.runner.Description
8 | import org.junit.runners.model.Statement
9 |
10 | @ExperimentalCoroutinesApi
11 | class TestCoroutineRule : TestRule {
12 |
13 | private val testCoroutineDispatcher = TestCoroutineDispatcher()
14 |
15 | private val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)
16 |
17 | override fun apply(base: Statement, description: Description?) = object : Statement() {
18 | @Throws(Throwable::class)
19 | override fun evaluate() {
20 | Dispatchers.setMain(testCoroutineDispatcher)
21 |
22 | base.evaluate()
23 |
24 | Dispatchers.resetMain()
25 | testCoroutineScope.cleanupTestCoroutines()
26 | }
27 | }
28 |
29 | fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
30 | testCoroutineScope.runBlockingTest { block() }
31 |
32 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/salesground/zipbolt/TimeUtilsTest.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt
2 |
3 | import com.salesground.zipbolt.utils.customizeDate
4 | import com.salesground.zipbolt.utils.formatVideoDurationToString
5 | import com.salesground.zipbolt.utils.parseDate
6 | import junit.framework.Assert.assertEquals
7 | import org.junit.Test
8 | import org.junit.runner.RunWith
9 | import org.junit.runners.JUnit4
10 | import java.time.LocalDate
11 |
12 |
13 | class TimeUtilsTest {
14 |
15 | @Test
16 | fun test_parseDate() {
17 | assertEquals("20 March, 2021", System.currentTimeMillis().parseDate())
18 | }
19 |
20 | @Test
21 | fun test_dateDifference() {
22 | val yesterday = System.currentTimeMillis() - (24 * 60 * 60 * 1000)
23 |
24 | assertEquals("Yesterday", yesterday.parseDate().customizeDate())
25 | }
26 |
27 | @Test
28 | fun test_formatVideoDuration() {
29 | assertEquals("59sec", 59000L.formatVideoDurationToString())
30 | assertEquals("1min 20sec", 80_000L.formatVideoDurationToString())
31 | assertEquals("23sec", 2300L.formatVideoDurationToString())
32 |
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/AudioViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.viewModelScope
7 | import com.salesground.zipbolt.model.DataToTransfer
8 | import com.salesground.zipbolt.repository.AudioRepository
9 | import dagger.hilt.android.lifecycle.HiltViewModel
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.withContext
13 | import javax.inject.Inject
14 |
15 | @HiltViewModel
16 | class AudioViewModel @Inject constructor(private val audioRepository: AudioRepository) :
17 | ViewModel() {
18 |
19 | private val _deviceAudio = MutableLiveData>()
20 | val deviceAudio: LiveData>
21 | get() = _deviceAudio
22 |
23 | fun getDeviceAudio() {
24 | viewModelScope.launch(Dispatchers.IO) {
25 | val deviceAudio = audioRepository.getAudioOnDevice()
26 | withContext(Dispatchers.Main) {
27 | _deviceAudio.value = deviceAudio
28 | }
29 | }
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/plain_file_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/app/src/main/res/layout/expanded_connected_to_peer_transfer_ongoing.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
20 |
21 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/collapsed_connected_to_peer_transfer_receive_ongoing_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/VideoViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.viewModelScope
7 | import com.salesground.zipbolt.model.DataToTransfer
8 | import com.salesground.zipbolt.repository.VideoRepositoryI
9 | import dagger.hilt.android.lifecycle.HiltViewModel
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.withContext
13 | import javax.inject.Inject
14 |
15 | @HiltViewModel
16 | class VideoViewModel @Inject constructor(
17 | private val videoRepositoryI: VideoRepositoryI
18 | ) : ViewModel() {
19 |
20 | private val _allVideosOnDevice = MutableLiveData>(mutableListOf())
21 | val allVideosOnDevice: LiveData>
22 | get() = _allVideosOnDevice
23 |
24 |
25 | fun getAllVideosOnDevice() {
26 | viewModelScope.launch(Dispatchers.IO) {
27 | val videosOnDevice = videoRepositoryI.getVideosOnDevice()
28 | withContext(Dispatchers.Main) {
29 | _allVideosOnDevice.value = videosOnDevice
30 | }
31 | }
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/SentAndReceivedDataItemsViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.lifecycle.Lifecycle
6 | import androidx.viewpager2.adapter.FragmentStateAdapter
7 | import com.salesground.zipbolt.ui.fragments.ReceivedDataFragment
8 | import com.salesground.zipbolt.ui.fragments.SentDataFragment
9 |
10 | class SentAndReceiveDataItemsViewPagerAdapter(
11 | fragmentManager: FragmentManager, lifecycle: Lifecycle,
12 | private val isSender: Boolean
13 | ) :
14 | FragmentStateAdapter(fragmentManager, lifecycle) {
15 |
16 | override fun getItemCount(): Int {
17 | return 2
18 | }
19 |
20 |
21 | override fun createFragment(position: Int): Fragment {
22 | return when {
23 | isSender && position == 0 -> {
24 | SentDataFragment()
25 | }
26 | isSender && position == 1 -> {
27 | ReceivedDataFragment()
28 | }
29 | !isSender && position == 0 -> {
30 | ReceivedDataFragment()
31 | }
32 | else -> {
33 | SentDataFragment()
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/globe_icon.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF2E9DFF
9 | #302E9DFF
10 | #FFFF6859
11 | #54E369
12 | #FF000000
13 | #10000000
14 | #FFFFFFFF
15 | #FF121212
16 | #616161
17 | #FF121212
18 | #E0E0E0
19 | #FFFFFFFF
20 | #FF121212
21 | #2E7D57
22 | #CC992D
23 | #CC462A
24 | #FCB030
25 | #FF6F50
26 |
--------------------------------------------------------------------------------
/app/src/test/java/com/salesground/zipbolt/LiveDataTestUtils.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt
2 |
3 | import androidx.annotation.VisibleForTesting
4 | import androidx.lifecycle.LiveData
5 | import androidx.lifecycle.Observer
6 | import java.util.concurrent.CountDownLatch
7 | import java.util.concurrent.TimeUnit
8 | import java.util.concurrent.TimeoutException
9 |
10 |
11 | @VisibleForTesting(otherwise = VisibleForTesting.NONE)
12 | fun LiveData.getOrAwaitValue(
13 | time: Long = 2,
14 | timeUnit: TimeUnit = TimeUnit.SECONDS,
15 | afterObserve: () -> Unit = {}
16 | ): T {
17 | var data: T? = null
18 | val latch = CountDownLatch(1)
19 | val observer = object : Observer {
20 | override fun onChanged(o: T?) {
21 | data = o
22 | latch.countDown()
23 | this@getOrAwaitValue.removeObserver(this)
24 | }
25 | }
26 | this.observeForever(observer)
27 |
28 | try {
29 | afterObserve.invoke()
30 |
31 | // Don't wait indefinitely if the LiveData is not set.
32 | if (!latch.await(time, timeUnit)) {
33 | throw TimeoutException("LiveData value was never set.")
34 | }
35 |
36 | } finally {
37 | this.removeObserver(observer)
38 | }
39 |
40 | @Suppress("UNCHECKED_CAST")
41 | return data as T
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/videoFragment/VideoFragmentRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.videoFragment
2 |
3 | import android.view.ViewGroup
4 | import androidx.recyclerview.widget.ListAdapter
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.salesground.zipbolt.model.DataToTransfer
7 | import com.salesground.zipbolt.ui.recyclerview.DataToTransferRecyclerViewDiffUtil
8 | import com.salesground.zipbolt.ui.recyclerview.RecyclerViewItemClickedListener
9 |
10 | class VideoFragmentRecyclerViewAdapter(
11 | private val videoLayoutClickedListener:
12 | RecyclerViewItemClickedListener,
13 | var selectedVideos: MutableList
14 | ) : ListAdapter(DataToTransferRecyclerViewDiffUtil()) {
15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
16 | return VideoLayoutItemViewHolder.createViewHolder(
17 | parent,
18 | videoLayoutClickedListener
19 | )
20 | }
21 |
22 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
23 | if (holder is VideoLayoutItemViewHolder) {
24 | holder.bindVideoData(getItem(position), selectedVideos)
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/audioFragment/AudioFragmentRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.audioFragment
2 |
3 | import android.view.ViewGroup
4 | import androidx.recyclerview.widget.ListAdapter
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.salesground.zipbolt.model.DataToTransfer
7 | import com.salesground.zipbolt.ui.recyclerview.DataToTransferRecyclerViewDiffUtil
8 | import com.salesground.zipbolt.ui.recyclerview.RecyclerViewItemClickedListener
9 |
10 | class AudioFragmentRecyclerViewAdapter(
11 | private val audioLayoutClickedListener:
12 | RecyclerViewItemClickedListener,
13 | var selectedAudios: MutableList
14 | ) : ListAdapter(DataToTransferRecyclerViewDiffUtil()) {
15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
16 | return AudioLayoutItemViewHolder.createViewHolder(
17 | parent,
18 | audioLayoutClickedListener
19 | )
20 | }
21 |
22 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
23 | if (holder is AudioLayoutItemViewHolder) {
24 | holder.bindData(getItem(position), selectedAudios)
25 | }
26 | }
27 |
28 |
29 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_files.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
24 |
25 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/customviews/ConstraintLayoutWithTopBorderLine.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.customviews
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Paint
6 | import android.util.AttributeSet
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import androidx.core.content.ContextCompat
9 | import com.salesground.zipbolt.R
10 |
11 | class ConstraintLayoutWithTopBorderLine @JvmOverloads constructor(
12 | context: Context, attrs: AttributeSet? = null
13 | ) : ConstraintLayout(context, attrs) {
14 |
15 | private val borderLinePaint = Paint().apply {
16 | style = Paint.Style.STROKE
17 | strokeWidth = 0.85f * resources.displayMetrics.density
18 | strokeJoin = Paint.Join.ROUND
19 | strokeCap = Paint.Cap.ROUND
20 | isDither = true
21 | isAntiAlias = true
22 | alpha = 75
23 | color = ContextCompat.getColor(context, R.color.onBackgroundColor)
24 | }
25 |
26 | override fun dispatchDraw(canvas: Canvas?) {
27 | super.dispatchDraw(canvas)
28 | canvas?.drawLine(
29 | paddingLeft.toFloat(), paddingTop.toFloat(),
30 | (measuredWidth - paddingRight).toFloat(),
31 | paddingTop.toFloat(),
32 | borderLinePaint
33 | )
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/broadcast/DataTransferServiceConnectionStateReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.broadcast
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 |
7 | class DataTransferServiceConnectionStateReceiver(
8 | private val connectionStateListener: ConnectionStateListener
9 | ) : BroadcastReceiver() {
10 |
11 | companion object {
12 | const val ACTION_DISCONNECTED_FROM_PEER = "DataTransferServiceActionDisconnectedFromPeer"
13 | const val ACTION_CANNOT_CONNECT_TO_PEER_ADDRESS =
14 | "DataTransferServiceActionCannotConnectToPeerAddress"
15 | }
16 |
17 | interface ConnectionStateListener {
18 | fun disconnectedFromPeer()
19 | fun cannotConnectToPeerAddress()
20 | fun connectionBroken()
21 | }
22 |
23 | override fun onReceive(context: Context?, intent: Intent?) {
24 | intent?.let {
25 | when (intent.action) {
26 | ACTION_DISCONNECTED_FROM_PEER -> {
27 | connectionStateListener.disconnectedFromPeer()
28 | }
29 | ACTION_CANNOT_CONNECT_TO_PEER_ADDRESS -> {
30 | connectionStateListener.cannotConnectToPeerAddress()
31 | }
32 | }
33 | }
34 | }
35 |
36 |
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/di/RepositoryDIModule.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.di
2 |
3 | import com.salesground.zipbolt.repository.*
4 | import com.salesground.zipbolt.repository.implementation.*
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.components.SingletonComponent
9 | import javax.inject.Singleton
10 |
11 | @InstallIn(SingletonComponent::class)
12 | @Module
13 | abstract class RepositoryDIModule {
14 | @Singleton
15 | @Binds
16 | abstract fun getZipBoltImageRepository(advanceImageRepository: AdvanceImageRepository): ImageRepository
17 |
18 | @Singleton
19 | @Binds
20 | abstract fun getZipBoltSavedFilesRepository(zipBoltSavedFilesRepository: ZipBoltSavedFilesRepository): SavedFilesRepository
21 |
22 | @Singleton
23 | @Binds
24 | abstract fun getAapplicationsRepository(deviceApplicationsRepository: DeviceApplicationsRepository): ApplicationsRepositoryInterface
25 |
26 | @Singleton
27 | @Binds
28 | abstract fun getZipBoltVideoRepository(zipBoltVideoRepository: ZipBoltVideoRepository): VideoRepositoryI
29 |
30 | @Singleton
31 | @Binds
32 | abstract fun getZipBoltAudioRepository(zipBoltAudioRepository: ZipBoltAudioRepository): AudioRepository
33 |
34 | @Singleton
35 | @Binds
36 | abstract fun getZipBoltFileRepository(zipBoltFileRepository: ZipBoltFileRepository): FileRepository
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/image/ImageTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.image
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.bumptech.glide.Glide
7 | import com.salesground.zipbolt.databinding.ImageTransferLayoutItemBinding
8 | import com.salesground.zipbolt.model.DataToTransfer
9 |
10 | class ImageTransferWaitingLayoutItemViewHolder(
11 | private val imageTransferLayoutItemBinding: ImageTransferLayoutItemBinding
12 | ) : RecyclerView.ViewHolder(imageTransferLayoutItemBinding.root) {
13 |
14 | fun bindImageData(imageData: DataToTransfer) {
15 | imageTransferLayoutItemBinding.apply {
16 | Glide.with(imageWaitingForTransferLayoutItemImageView)
17 | .load(imageData.dataUri)
18 | .into(imageWaitingForTransferLayoutItemImageView)
19 | }
20 | }
21 |
22 | companion object {
23 | fun createViewHolder(parent: ViewGroup): ImageTransferWaitingLayoutItemViewHolder {
24 | val layoutBinding = ImageTransferLayoutItemBinding.inflate(
25 | LayoutInflater.from(parent.context),
26 | parent,
27 | false
28 | )
29 | return ImageTransferWaitingLayoutItemViewHolder(layoutBinding)
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/implementation/ZipBoltFileRepository.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository.implementation
2 |
3 | import android.content.Context
4 | import android.os.Environment
5 | import com.salesground.zipbolt.communication.MediaTransferProtocol
6 | import com.salesground.zipbolt.model.DataToTransfer
7 | import com.salesground.zipbolt.model.MediaType
8 | import com.salesground.zipbolt.repository.FileRepository
9 | import com.salesground.zipbolt.service.DataTransferService
10 | import dagger.hilt.android.qualifiers.ApplicationContext
11 | import java.io.BufferedOutputStream
12 | import java.io.DataInputStream
13 | import java.io.File
14 | import java.io.FileOutputStream
15 | import javax.inject.Inject
16 | import kotlin.math.min
17 |
18 | class ZipBoltFileRepository @Inject constructor(
19 | ) : FileRepository {
20 | private val dataBuffer = ByteArray(DataTransferService.BUFFER_SIZE)
21 | private var sizeOfDataReadFromDirectoryTransfer: Long = 0L
22 | override suspend fun getRootDirectory(): File {
23 | return Environment.getExternalStorageDirectory()
24 | }
25 |
26 | override suspend fun getDirectoryChildren(directoryPath: String): List {
27 | var children = listOf()
28 | File(directoryPath).apply {
29 | children = listFiles()?.map {
30 | DataToTransfer.DeviceFile(it)
31 | } ?: listOf()
32 | }
33 | return children
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/imagefragment/DateModifiedHeaderViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.imagefragment
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.MediaDateModifiedHeaderBinding
9 | import com.salesground.zipbolt.model.ui.ImagesDisplayModel
10 |
11 | class DateModifiedHeaderViewHolder(
12 | private val mediaDateModifiedHeaderBinding:
13 | MediaDateModifiedHeaderBinding
14 | ) : RecyclerView.ViewHolder(mediaDateModifiedHeaderBinding.root) {
15 |
16 | fun bindDateModified(dateModified: ImagesDisplayModel.ImagesDateModifiedHeader) {
17 | mediaDateModifiedHeaderBinding.apply {
18 | this.dateModified = dateModified.dateModified
19 | executePendingBindings()
20 | }
21 | }
22 |
23 | companion object {
24 | fun createDateModifiedHeaderViewHolder(parent: ViewGroup): DateModifiedHeaderViewHolder {
25 | val layoutItemBinding = DataBindingUtil.inflate(
26 | LayoutInflater.from(parent.context),
27 | R.layout.media_date_modified_header,
28 | parent,
29 | false
30 | )
31 | return DateModifiedHeaderViewHolder(layoutItemBinding)
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/ApplicationsViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.viewModelScope
7 | import com.salesground.zipbolt.model.DataToTransfer
8 | import com.salesground.zipbolt.repository.ApplicationsRepositoryInterface
9 | import dagger.hilt.android.lifecycle.HiltViewModel
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.withContext
13 | import javax.inject.Inject
14 |
15 | @HiltViewModel
16 | class ApplicationsViewModel @Inject constructor(
17 | private val applicationsRepositoryInterface: ApplicationsRepositoryInterface
18 | ) : ViewModel() {
19 |
20 | private var allNonSystemAppsOnDevice = listOf()
21 | private val _allApplicationsOnDevice = MutableLiveData>(
22 | listOf()
23 | )
24 | val allApplicationsOnDevice: LiveData>
25 | get() = _allApplicationsOnDevice
26 |
27 |
28 |
29 | fun getAllApplicationsOnDevice() {
30 | viewModelScope.launch(Dispatchers.IO) {
31 | allNonSystemAppsOnDevice = applicationsRepositoryInterface.getNonSystemAppsOnDevice()
32 | withContext(Dispatchers.Main) {
33 | _allApplicationsOnDevice.value = allNonSystemAppsOnDevice
34 | }
35 | }
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/discovered_peer_layout_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
20 |
21 |
26 |
27 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/PlainFileTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainFileTransferWaitingLayoutItemViewHolder(
12 | private val plainFileTransferLayoutItemBinding: PlainFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | }
19 | }
20 |
21 | companion object {
22 | fun createViewHolder(parent: ViewGroup): PlainFileTransferWaitingLayoutItemViewHolder {
23 | val layoutBinding = DataBindingUtil.inflate(
24 | LayoutInflater.from(parent.context),
25 | R.layout.plain_file_transfer_layout_item,
26 | parent,
27 | false
28 | )
29 | return PlainFileTransferWaitingLayoutItemViewHolder(layoutBinding)
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/PeersDiscoveryViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import android.net.wifi.p2p.WifiP2pDevice
4 | import androidx.lifecycle.LiveData
5 | import androidx.lifecycle.MutableLiveData
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.viewModelScope
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.launch
11 |
12 |
13 | class PeersDiscoveryViewModel : ViewModel() {
14 | val deviceTransferTypeMap = HashMap()
15 | private var normalDiscoveredPeerSet = mutableSetOf()
16 | private val _discoveredPeerSet = MutableLiveData>(null)
17 | val discoveredPeerSet: LiveData>
18 | get() = _discoveredPeerSet
19 |
20 | fun addDiscoveredDevice(wifiP2pDevice: WifiP2pDevice) {
21 | if (normalDiscoveredPeerSet.find {
22 | it.deviceAddress == wifiP2pDevice.deviceAddress
23 | } == null) {
24 | normalDiscoveredPeerSet.add(wifiP2pDevice)
25 | viewModelScope.launch(Dispatchers.Main) {
26 | _discoveredPeerSet.value = normalDiscoveredPeerSet
27 | }
28 | }
29 | }
30 |
31 | fun clearDiscoveredPeerSet() {
32 | normalDiscoveredPeerSet = mutableSetOf()
33 | viewModelScope.launch(Dispatchers.Main) {
34 | _discoveredPeerSet.value = normalDiscoveredPeerSet
35 | }
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/applicationFragment/ApplicationFragmentAppsDisplayRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.applicationFragment
2 |
3 | import android.view.ViewGroup
4 | import androidx.recyclerview.widget.ListAdapter
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.salesground.zipbolt.model.DataToTransfer
7 | import com.salesground.zipbolt.ui.recyclerview.DataToTransferRecyclerViewDiffUtil
8 | import com.salesground.zipbolt.ui.recyclerview.RecyclerViewItemClickedListener
9 |
10 | class ApplicationFragmentAppsDisplayRecyclerViewAdapter(
11 | private val applicationLayoutClickedListener:
12 | RecyclerViewItemClickedListener,
13 | var selectedApplications: MutableList
14 | ) : ListAdapter(
15 | DataToTransferRecyclerViewDiffUtil()
16 | ) {
17 |
18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
19 | return ApplicationLayoutItemViewHolder.createViewHolder(
20 | parent,
21 | applicationLayoutClickedListener
22 | )
23 | }
24 |
25 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
26 | if (holder is ApplicationLayoutItemViewHolder) {
27 | val currentItem = getItem(position)
28 | holder.bindApplicationDetails(
29 | currentItem,
30 | selectedApplications
31 | )
32 | }
33 | }
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/zip_bolt_header_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/image/PlainImageFileTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile.image
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainImageFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainImageFileTransferWaitingLayoutItemViewHolder(
12 | private val plainImageFileTransferLayoutItemBinding: PlainImageFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainImageFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainImageFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | }
19 | }
20 |
21 | companion object {
22 | fun createViewHolder(parent: ViewGroup): PlainImageFileTransferWaitingLayoutItemViewHolder {
23 | val layoutBinding = DataBindingUtil.inflate(
24 | LayoutInflater.from(parent.context),
25 | R.layout.plain_image_file_transfer_layout_item,
26 | parent,
27 | false
28 | )
29 | return PlainImageFileTransferWaitingLayoutItemViewHolder(layoutBinding)
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/video/PlainVideoFileTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile.video
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainVideoFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainVideoFileTransferWaitingLayoutItemViewHolder(
12 | private val plainVideoFileTransferLayoutItemBinding: PlainVideoFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainVideoFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainVideoFileTransferLayoutItemBinding.run {
17 | this.document = dataToTransfer as DataToTransfer.DeviceFile
18 | }
19 | }
20 |
21 | companion object {
22 | fun createViewHolder(parent: ViewGroup): PlainVideoFileTransferWaitingLayoutItemViewHolder {
23 | val layoutBinding = DataBindingUtil.inflate(
24 | LayoutInflater.from(parent.context),
25 | R.layout.plain_video_file_transfer_layout_item,
26 | parent,
27 | false
28 | )
29 | return PlainVideoFileTransferWaitingLayoutItemViewHolder(layoutBinding)
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pdf.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/di/AndroidComponentsDIModule.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.di
2 |
3 | import android.app.NotificationManager
4 | import android.content.Context
5 | import android.net.ConnectivityManager
6 | import android.net.wifi.WifiManager
7 | import android.net.wifi.p2p.WifiP2pManager
8 | import androidx.localbroadcastmanager.content.LocalBroadcastManager
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.android.qualifiers.ApplicationContext
13 | import dagger.hilt.components.SingletonComponent
14 | import javax.inject.Singleton
15 |
16 | @InstallIn(SingletonComponent::class)
17 | @Module
18 | class AndroidComponentsDIModule {
19 |
20 | @Provides
21 | fun getNotificationManager(@ApplicationContext context: Context): NotificationManager {
22 | return context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
23 | }
24 |
25 | @Provides
26 | @Singleton
27 | fun getLocalBroadcastManager(@ApplicationContext context: Context): LocalBroadcastManager {
28 | return LocalBroadcastManager.getInstance(context)
29 | }
30 |
31 | @Provides
32 | @Singleton
33 | fun getWifiP2pManager(@ApplicationContext context: Context): WifiP2pManager {
34 | return context.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
35 | }
36 |
37 | @Provides
38 | @Singleton
39 | fun getWifiManager(@ApplicationContext context: Context): WifiManager {
40 | return context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/document/PlainDocumentFileTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile.document
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainDocumentFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainDocumentFileTransferWaitingLayoutItemViewHolder(
12 | private val plainDocumentFileTransferLayoutItemBinding: PlainDocumentFileTransferLayoutItemBinding
13 | ): RecyclerView.ViewHolder(plainDocumentFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer){
16 | plainDocumentFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | }
19 | }
20 |
21 | companion object{
22 | fun createViewHolder(parent: ViewGroup): PlainDocumentFileTransferWaitingLayoutItemViewHolder{
23 | val layoutBinding = DataBindingUtil.inflate(
24 | LayoutInflater.from(parent.context),
25 | R.layout.plain_document_file_transfer_layout_item,
26 | parent,
27 | false
28 | )
29 | return PlainDocumentFileTransferWaitingLayoutItemViewHolder(layoutBinding)
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/image/ImageTransferCompleteLayoutViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.image
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.bumptech.glide.Glide
7 | import com.salesground.zipbolt.databinding.ImageTransferLayoutItemBinding
8 | import com.salesground.zipbolt.model.DataToTransfer
9 |
10 | class ImageTransferCompleteLayoutViewHolder(
11 | private val imageTransferLayoutItemBinding: ImageTransferLayoutItemBinding
12 | ) : RecyclerView.ViewHolder(imageTransferLayoutItemBinding.root) {
13 |
14 | fun bindImageData(dataToTransfer: DataToTransfer) {
15 | imageTransferLayoutItemBinding.run {
16 | Glide.with(imageWaitingForTransferLayoutItemImageView)
17 | .load(dataToTransfer.dataUri)
18 | .into(imageWaitingForTransferLayoutItemImageView)
19 |
20 | imageTransferLayoutItemLoadingImageShimmer.run{
21 | stopShimmer()
22 | hideShimmer()
23 | }
24 | }
25 | }
26 |
27 | companion object {
28 | fun createViewHolder(parent: ViewGroup): ImageTransferCompleteLayoutViewHolder {
29 | val layoutBinding = ImageTransferLayoutItemBinding.inflate(
30 | LayoutInflater.from(parent.context),
31 | parent,
32 | false
33 | )
34 |
35 | return ImageTransferCompleteLayoutViewHolder(layoutBinding)
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/image/ImageReceiveCompleteLayoutViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.receivedDataFragment.viewHolders.image
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.bumptech.glide.Glide
7 | import com.salesground.zipbolt.databinding.ImageTransferLayoutItemBinding
8 | import com.salesground.zipbolt.model.DataToTransfer
9 |
10 | class ImageReceiveCompleteLayoutViewHolder(
11 | private val imageReceiveLayoutItemBinding: ImageTransferLayoutItemBinding
12 | ) : RecyclerView.ViewHolder(imageReceiveLayoutItemBinding.root) {
13 |
14 | fun bindImageData(dataToTransfer: DataToTransfer) {
15 | imageReceiveLayoutItemBinding.run {
16 | Glide.with(imageWaitingForTransferLayoutItemImageView)
17 | .load(dataToTransfer.dataUri)
18 | .into(imageWaitingForTransferLayoutItemImageView)
19 |
20 | imageTransferLayoutItemLoadingImageShimmer.run {
21 | stopShimmer()
22 | hideShimmer()
23 | }
24 | }
25 | }
26 |
27 | companion object {
28 | fun createViewHolder(parent: ViewGroup): ImageReceiveCompleteLayoutViewHolder {
29 | val layoutBinding =
30 | ImageTransferLayoutItemBinding.inflate(
31 | LayoutInflater.from(parent.context),
32 | parent,
33 | false
34 | )
35 |
36 | return ImageReceiveCompleteLayoutViewHolder(layoutBinding)
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/PlainFileTransferCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainFileTransferCompleteLayoutItemViewHolder(
12 | private val plainFileTransferLayoutItemBinding: PlainFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | plainFileTransferLayoutItemShimmer.run {
19 | stopShimmer()
20 | hideShimmer()
21 | }
22 | }
23 | }
24 |
25 | companion object {
26 | fun createViewHolder(parent: ViewGroup): PlainFileTransferCompleteLayoutItemViewHolder {
27 | val layoutBinding = DataBindingUtil.inflate(
28 | LayoutInflater.from(parent.context),
29 | R.layout.plain_file_transfer_layout_item,
30 | parent,
31 | false
32 | )
33 | return PlainFileTransferCompleteLayoutItemViewHolder(layoutBinding)
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/peersDiscoveryFragment/PeersDiscoveredRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.peersDiscoveryFragment
2 |
3 | import android.net.wifi.p2p.WifiP2pDevice
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.DiffUtil
6 | import androidx.recyclerview.widget.ListAdapter
7 | import androidx.recyclerview.widget.RecyclerView
8 | import com.salesground.zipbolt.ui.recyclerview.RecyclerViewItemClickedListener
9 |
10 | class PeersDiscoveredRecyclerViewAdapter(
11 | private val deviceClickedListener: RecyclerViewItemClickedListener
12 | ) : ListAdapter(PeersDiscoveredRecyclerViewAdapterDiffUtil) {
14 |
15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
16 | return DiscoveredPeerLayoutItemViewHolder.createViewHolder(
17 | parent,
18 | deviceClickedListener
19 | )
20 | }
21 |
22 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
23 | if (holder is DiscoveredPeerLayoutItemViewHolder) {
24 | holder.bindData(getItem(position))
25 | }
26 | }
27 | }
28 |
29 | object PeersDiscoveredRecyclerViewAdapterDiffUtil : DiffUtil.ItemCallback() {
30 | override fun areItemsTheSame(oldItem: WifiP2pDevice, newItem: WifiP2pDevice): Boolean {
31 | return oldItem.deviceAddress == newItem.deviceAddress
32 | }
33 |
34 | override fun areContentsTheSame(oldItem: WifiP2pDevice, newItem: WifiP2pDevice): Boolean {
35 | return oldItem == newItem
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/directory/DirectoryTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.directory
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.FolderTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 | import com.salesground.zipbolt.utils.getDirectorySize
11 |
12 | class DirectoryTransferWaitingLayoutItemViewHolder(
13 | private val folderTransferLayoutItemBinding: FolderTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(folderTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceFile
18 | folderTransferLayoutItemBinding.run {
19 | folderSize = dataToTransfer.file.getDirectorySize()
20 | folderName = dataToTransfer.dataDisplayName
21 |
22 | }
23 | }
24 |
25 | companion object {
26 | fun createViewHolder(parent: ViewGroup): DirectoryTransferWaitingLayoutItemViewHolder {
27 | val layoutBinding = DataBindingUtil.inflate(
28 | LayoutInflater.from(parent.context),
29 | R.layout.folder_transfer_layout_item,
30 | parent,
31 | false
32 | )
33 |
34 | return DirectoryTransferWaitingLayoutItemViewHolder(layoutBinding)
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/repository/implementation/ZipBoltSavedFilesRepository.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 | /*
3 | * Overview
4 | * This class is responsible for generating the base directories for other repositories like
5 | * ImageRepositoryInitial, VideoRepository etc to saved the files of received items before inserting
6 | * them into the media store of the various category
7 | *
8 | * Functions description
9 | * 1). getZipBoltBaseDirectory() returns the top level ZipBolt directory in the file system
10 | * 2). getZipBoltMediaCategoryBaseDirectory() returns the mediaType directory under the main directory
11 | * 3). checkIfDirectory() exists confirm that a given directory exists, if not then it creates it
12 | * */
13 | import android.os.Environment
14 | import java.io.File
15 | import javax.inject.Inject
16 |
17 | const val ZIP_BOLT_MAIN_DIRECTORY = "ZipBolt"
18 |
19 | class ZipBoltSavedFilesRepository @Inject constructor() : SavedFilesRepository {
20 |
21 | override fun getZipBoltBaseDirectory(): File {
22 | val baseDirectory = File(Environment.getExternalStorageDirectory(), ZIP_BOLT_MAIN_DIRECTORY)
23 | checkIfDirectoryExist(baseDirectory)
24 | return baseDirectory
25 | }
26 |
27 | override fun getZipBoltMediaCategoryBaseDirectory(categoryType: SavedFilesRepository.ZipBoltMediaCategory): File {
28 | val categoryBaseDirectory = File(getZipBoltBaseDirectory(), categoryType.categoryName)
29 | checkIfDirectoryExist(categoryBaseDirectory)
30 | return categoryBaseDirectory
31 | }
32 |
33 | private fun checkIfDirectoryExist(directory: File) {
34 | if (!directory.exists()) directory.mkdirs()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/salesground/zipbolt/repository/ZipBoltSavedFilesRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import com.salesground.zipbolt.repository.SavedFilesRepository.*
4 | import org.junit.Before
5 |
6 | import org.junit.Assert.*
7 | import org.junit.Test
8 | import java.util.zip.ZipInputStream
9 |
10 | class ZipBoltSavedFilesRepositoryTest {
11 | lateinit var zipBoltSavedFilesRepository: ZipBoltSavedFilesRepository
12 |
13 | @Before
14 | fun setUp() {
15 | zipBoltSavedFilesRepository = ZipBoltSavedFilesRepository()
16 | }
17 |
18 | @Test
19 | fun confirmThatTheAppRootDirectoryNameIsZipBolt() {
20 | val imageBaseDirectory = zipBoltSavedFilesRepository
21 | .getZipBoltMediaCategoryBaseDirectory(ZipBoltMediaCategory.IMAGES_BASE_DIRECTORY)
22 | assertEquals(imageBaseDirectory.parentFile.name, "ZipBolt")
23 | }
24 |
25 | @Test
26 | fun confirmThatDirectoriesAreCreatedForEachMediaCategory() {
27 | val mediaCategory = ZipBoltMediaCategory.values()
28 | mediaCategory.forEach {
29 | val mediaCategory =
30 | zipBoltSavedFilesRepository.getZipBoltMediaCategoryBaseDirectory(it)
31 | assertTrue(mediaCategory.exists())
32 | }
33 | }
34 |
35 | @Test
36 | fun confirmThatDirectoryForImagesHasTheNameImages(){
37 | val imageCategory = ZipBoltMediaCategory.IMAGES_BASE_DIRECTORY
38 | val imageCategoryBaseDirectory =
39 | zipBoltSavedFilesRepository.getZipBoltMediaCategoryBaseDirectory(imageCategory)
40 | assertEquals(imageCategoryBaseDirectory.name, ZipBoltMediaCategory.IMAGES_BASE_DIRECTORY.categoryName)
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/AllMediaOnDeviceViewPager2Adapter.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 | import androidx.lifecycle.Lifecycle
7 | import androidx.viewpager2.adapter.FragmentStateAdapter
8 | import com.salesground.zipbolt.ui.fragments.*
9 |
10 | class AllMediaOnDeviceViewPager2Adapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
11 | FragmentStateAdapter(fragmentManager, lifecycle) {
12 |
13 | override fun getItemCount(): Int {
14 | return 5
15 | }
16 |
17 |
18 | override fun createFragment(position: Int): Fragment {
19 | return when (position) {
20 | 0 -> DeviceAppsFragment()
21 | 1 -> ImageFragment()
22 | 2 -> VideosFragment()
23 | 3 -> AudioFragment()
24 | 4 -> FilesFragment()
25 | else -> ImageFragment()
26 | }
27 | }
28 | }
29 |
30 | class AllMediaOnDeviceViewPagerAdapter(private val fragmentManager: FragmentManager) :
31 | FragmentStatePagerAdapter(
32 | fragmentManager,
33 | FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
34 | ) {
35 | override fun getItem(position: Int): Fragment {
36 | return when (position) {
37 | 0 -> DeviceAppsFragment()
38 | 1 -> ImageFragment()
39 | 2 -> VideosFragment()
40 | 3 -> AudioFragment()
41 | 4 -> FilesFragment()
42 | else -> ImageFragment()
43 | }
44 | }
45 |
46 | override fun getCount(): Int {
47 | return 5
48 | }
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/video/PlainVideoFileTransferCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile.video
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainVideoFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainVideoFileTransferCompleteLayoutItemViewHolder(
12 | private val plainVideoFileTransferLayoutItemBinding: PlainVideoFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainVideoFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainVideoFileTransferLayoutItemBinding.run {
17 | this.document = dataToTransfer as DataToTransfer.DeviceFile
18 | plainVideoFileTransferLayoutItemShimmer.run {
19 | stopShimmer()
20 | hideShimmer()
21 | }
22 | }
23 | }
24 |
25 | companion object {
26 | fun createViewHolder(parent: ViewGroup): PlainVideoFileTransferCompleteLayoutItemViewHolder {
27 | val layoutBinding = DataBindingUtil.inflate(
28 | LayoutInflater.from(parent.context),
29 | R.layout.plain_video_file_transfer_layout_item,
30 | parent,
31 | false
32 | )
33 | return PlainVideoFileTransferCompleteLayoutItemViewHolder(layoutBinding)
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/document/PlainDocumentFileTransferCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile.document
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainDocumentFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainDocumentFileTransferCompleteLayoutItemViewHolder(
12 | private val plainDocumentFileTransferLayoutItemBinding: PlainDocumentFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainDocumentFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer){
16 | plainDocumentFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | plainDocumentFileTransferLayoutItemShimmer.run {
19 | stopShimmer()
20 | hideShimmer()
21 | }
22 | }
23 | }
24 | companion object {
25 | fun createViewHolder(parent: ViewGroup): PlainDocumentFileTransferCompleteLayoutItemViewHolder {
26 | val layoutBinding = DataBindingUtil.inflate(
27 | LayoutInflater.from(parent.context),
28 | R.layout.plain_document_file_transfer_layout_item,
29 | parent,
30 | false
31 | )
32 | return PlainDocumentFileTransferCompleteLayoutItemViewHolder(layoutBinding)
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/plainFile/image/PlainImageFileTransferCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile.image
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainImageFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainImageFileTransferCompleteLayoutItemViewHolder(
12 | private val plainImageFileTransferLayoutItemBinding: PlainImageFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainImageFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainImageFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | plainImageFileTransferLayoutItemShimmer.run {
19 | stopShimmer()
20 | hideShimmer()
21 | }
22 | }
23 | }
24 |
25 | companion object {
26 | fun createViewHolder(parent: ViewGroup): PlainImageFileTransferCompleteLayoutItemViewHolder {
27 | val layoutBinding = DataBindingUtil.inflate(
28 | LayoutInflater.from(parent.context),
29 | R.layout.plain_image_file_transfer_layout_item,
30 | parent,
31 | false
32 | )
33 | return PlainImageFileTransferCompleteLayoutItemViewHolder(
34 | layoutBinding
35 | )
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/plainFile/document/PlainDocumentFileReceiveCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.receivedDataFragment.viewHolders.plainFile.document
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainDocumentFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainDocumentFileReceiveCompleteLayoutItemViewHolder(
12 | private val plainDocumentFileTransferLayoutItemBinding: PlainDocumentFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainDocumentFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainDocumentFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | plainDocumentFileTransferLayoutItemShimmer.run {
19 | stopShimmer()
20 | hideShimmer()
21 | }
22 | }
23 | }
24 |
25 | companion object {
26 | fun createViewHolder(parent: ViewGroup): PlainDocumentFileReceiveCompleteLayoutItemViewHolder {
27 | val layoutBinding = DataBindingUtil.inflate(
28 | LayoutInflater.from(parent.context),
29 | R.layout.plain_document_file_transfer_layout_item,
30 | parent,
31 | false
32 | )
33 | return PlainDocumentFileReceiveCompleteLayoutItemViewHolder(layoutBinding)
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/plainFile/image/PlainImageFileReceiveCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.receivedDataFragment.viewHolders.plainFile.image
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainImageFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 |
11 | class PlainImageFileReceiveCompleteLayoutItemViewHolder(
12 | private val plainImageFileTransferLayoutItemBinding: PlainImageFileTransferLayoutItemBinding
13 | ) : RecyclerView.ViewHolder(plainImageFileTransferLayoutItemBinding.root) {
14 |
15 | fun bindData(dataToTransfer: DataToTransfer) {
16 | plainImageFileTransferLayoutItemBinding.run {
17 | document = dataToTransfer as DataToTransfer.DeviceFile
18 | plainImageFileTransferLayoutItemShimmer.run {
19 | stopShimmer()
20 | hideShimmer()
21 | }
22 | }
23 | }
24 |
25 | companion object {
26 | fun createViewHolder(parent: ViewGroup): PlainImageFileReceiveCompleteLayoutItemViewHolder {
27 | val layoutItemBinding =
28 | DataBindingUtil.inflate(
29 | LayoutInflater.from(parent.context),
30 | R.layout.plain_image_file_transfer_layout_item,
31 | parent,
32 | false
33 | )
34 | return PlainImageFileReceiveCompleteLayoutItemViewHolder(layoutItemBinding)
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/audio/AudioTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.audio
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.AudioTransferLayoutItemBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 |
12 | class AudioTransferWaitingLayoutItemViewHolder(
13 | private val audioTransferLayoutItemBinding: AudioTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(audioTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceAudio
18 | audioTransferLayoutItemBinding.run {
19 | this.dataToTransfer = dataToTransfer
20 |
21 | Glide.with(audioTransferLayoutItemVideoPreviewImageView)
22 | .load(dataToTransfer.dataUri)
23 | .error(R.drawable.ic_icons8_music)
24 | .into(audioTransferLayoutItemVideoPreviewImageView)
25 | }
26 | }
27 |
28 | companion object {
29 | fun createViewHolder(parent: ViewGroup): AudioTransferWaitingLayoutItemViewHolder {
30 | val layoutBinding = DataBindingUtil.inflate(
31 | LayoutInflater.from(parent.context),
32 | R.layout.audio_transfer_layout_item,
33 | parent,
34 | false
35 | )
36 | return AudioTransferWaitingLayoutItemViewHolder(layoutBinding)
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/directory/DirectoryReceiveCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.receivedDataFragment.viewHolders.directory
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.FolderTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 | import com.salesground.zipbolt.utils.getDirectorySize
11 |
12 | class DirectoryReceiveCompleteLayoutItemViewHolder(
13 | private val folderTransferLayoutItemBinding: FolderTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(folderTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceFile
18 | folderTransferLayoutItemBinding.run {
19 | folderSize = dataToTransfer.file.getDirectorySize()
20 | folderName = dataToTransfer.dataDisplayName
21 | folderTransferLayoutItemShimmer.run {
22 | stopShimmer()
23 | hideShimmer()
24 | }
25 | }
26 | }
27 |
28 | companion object {
29 | fun createViewHolder(parent: ViewGroup): DirectoryReceiveCompleteLayoutItemViewHolder {
30 | val layoutBinding = DataBindingUtil.inflate(
31 | LayoutInflater.from(parent.context),
32 | R.layout.folder_transfer_layout_item,
33 | parent,
34 | false
35 | )
36 | return DirectoryReceiveCompleteLayoutItemViewHolder(layoutBinding)
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/directory/DirectoryTransferCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.directory
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.FolderTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 | import com.salesground.zipbolt.utils.getDirectorySize
11 |
12 | class DirectoryTransferCompleteLayoutItemViewHolder(
13 | private val folderTransferLayoutItemBinding: FolderTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(folderTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceFile
18 | folderTransferLayoutItemBinding.run {
19 | folderSize = dataToTransfer.file.getDirectorySize()
20 | folderName = dataToTransfer.dataDisplayName
21 | folderTransferLayoutItemShimmer.run {
22 | stopShimmer()
23 | hideShimmer()
24 | }
25 | }
26 | }
27 |
28 | companion object {
29 | fun createViewHolder(parent: ViewGroup): DirectoryTransferCompleteLayoutItemViewHolder {
30 | val layoutBinding = DataBindingUtil.inflate(
31 | LayoutInflater.from(parent.context),
32 | R.layout.folder_transfer_layout_item,
33 | parent,
34 | false
35 | )
36 | return DirectoryTransferCompleteLayoutItemViewHolder(layoutBinding)
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/MainActivityViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import android.net.wifi.p2p.WifiP2pDevice
4 | import android.net.wifi.p2p.WifiP2pInfo
5 | import androidx.lifecycle.LiveData
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.ViewModel
8 | import com.salesground.zipbolt.model.DataToTransfer
9 | import com.salesground.zipbolt.model.ui.PeerConnectionUIState
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class MainActivityViewModel @Inject constructor() : ViewModel() {
15 |
16 | private var hasBeenNotifiedAboutReceive: Boolean = false
17 | private val _peerConnectionUIState =
18 | MutableLiveData(PeerConnectionUIState.NoConnectionUIAction)
19 | val peerConnectionUIState: LiveData
20 | get() = _peerConnectionUIState
21 |
22 | fun peerConnectionNoAction() {
23 | _peerConnectionUIState.value = PeerConnectionUIState.NoConnectionUIAction
24 | }
25 |
26 | fun collapsedConnectedToPeerTransferOngoing() {
27 | _peerConnectionUIState.value =
28 | PeerConnectionUIState.CollapsedConnectedToPeerTransferOngoing
29 | }
30 |
31 |
32 | fun expandedConnectedToPeerReceiveOngoing() {
33 | if (!hasBeenNotifiedAboutReceive) {
34 | expandedConnectedToPeerTransferOngoing()
35 | hasBeenNotifiedAboutReceive = true
36 | }
37 | }
38 |
39 | fun expandedConnectedToPeerTransferOngoing() {
40 | _peerConnectionUIState.value =
41 | PeerConnectionUIState.ExpandedConnectedToPeerTransferOngoing
42 | }
43 |
44 | fun totalFileReceiveComplete() {
45 | hasBeenNotifiedAboutReceive = false
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/communication/MediaTransferProtocol.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.communication
2 |
3 | import android.net.Uri
4 | import com.salesground.zipbolt.model.DataToTransfer
5 | import java.io.DataInputStream
6 | import java.io.DataOutputStream
7 |
8 | interface MediaTransferProtocol {
9 |
10 | enum class MediaTransferProtocolMetaData(val value: Int) {
11 | NO_DATA(200),
12 | DATA_AVAILABLE(201),
13 | CANCEL_ON_GOING_TRANSFER(203),
14 |
15 | CANCEL_ACTIVE_RECEIVE(204),
16 | CANCEL_ACTIVE_TRANSFER(205),
17 | KEEP_RECEIVING(206),
18 | KEEP_RECEIVING_BUT_CANCEL_ACTIVE_TRANSFER(207),
19 | PAUSE_ACTIVE_TRANSFER(208)
20 | }
21 |
22 | fun cancelCurrentTransfer(transferMetaData: MediaTransferProtocolMetaData)
23 |
24 | suspend fun transferMedia(
25 | dataToTransfer: DataToTransfer,
26 | dataOutputStream: DataOutputStream,
27 | dataTransferListener: DataTransferListener
28 | )
29 |
30 |
31 | suspend fun receiveMedia(
32 | dataInputStream: DataInputStream,
33 | dataReceiveListener: DataReceiveListener
34 | )
35 |
36 | interface DataTransferListener {
37 | fun onTransfer(
38 | dataToTransfer: DataToTransfer,
39 | percentTransferred: Float,
40 | transferStatus: DataToTransfer.TransferStatus
41 | )
42 | }
43 |
44 | interface DataReceiveListener {
45 | fun onReceive(
46 | dataDisplayName: String, dataSize: Long, percentageOfDataRead: Float, dataType: Int,
47 | dataUri: Uri?, dataTransferStatus: DataToTransfer.TransferStatus
48 | )
49 | }
50 |
51 | interface TransferMetaDataUpdateListener {
52 | fun onMetaTransferDataUpdate(mediaTransferProtocolMetaData: MediaTransferProtocolMetaData)
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/video/VideoTransferWaitingLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.video
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.VideoTransferLayoutItemBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 |
12 | class VideoTransferWaitingLayoutItemViewHolder(
13 | private val videoTransferLayoutItemBinding: VideoTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(videoTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceVideo
18 | videoTransferLayoutItemBinding.run {
19 | videoSize = dataToTransfer.videoSize
20 | videoDuration = dataToTransfer.videoDuration
21 | videoName = dataToTransfer.videoDisplayName
22 |
23 | Glide.with(videoTransferLayoutItemVideoPreviewImageView)
24 | .load(dataToTransfer.dataUri)
25 | .into(videoTransferLayoutItemVideoPreviewImageView)
26 | }
27 | }
28 |
29 | companion object {
30 | fun createViewHolder(parent: ViewGroup): VideoTransferWaitingLayoutItemViewHolder {
31 | val layoutBinding = DataBindingUtil.inflate(
32 | LayoutInflater.from(parent.context),
33 | R.layout.video_transfer_layout_item,
34 | parent,
35 | false
36 | )
37 | return VideoTransferWaitingLayoutItemViewHolder(layoutBinding)
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/plainFile/video/PlainVideoFileReceiveCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.receivedDataFragment.viewHolders.plainFile.video
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.salesground.zipbolt.R
8 | import com.salesground.zipbolt.databinding.PlainVideoFileTransferLayoutItemBinding
9 | import com.salesground.zipbolt.model.DataToTransfer
10 | import com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.plainFile.video.PlainVideoFileTransferCompleteLayoutItemViewHolder
11 |
12 | class PlainVideoFileReceiveCompleteLayoutItemViewHolder(
13 | private val plainVideoFileTransferLayoutItemBinding: PlainVideoFileTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(plainVideoFileTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | plainVideoFileTransferLayoutItemBinding.run {
18 | this.document = dataToTransfer as DataToTransfer.DeviceFile
19 | plainVideoFileTransferLayoutItemShimmer.run {
20 | stopShimmer()
21 | hideShimmer()
22 | }
23 | }
24 | }
25 |
26 | companion object {
27 | fun createViewHolder(parent: ViewGroup): PlainVideoFileReceiveCompleteLayoutItemViewHolder {
28 | val layoutBinding = DataBindingUtil.inflate(
29 | LayoutInflater.from(parent.context),
30 | R.layout.plain_video_file_transfer_layout_item,
31 | parent,
32 | false
33 | )
34 | return PlainVideoFileReceiveCompleteLayoutItemViewHolder(layoutBinding)
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/audio/AudioTransferCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.audio
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.AudioTransferLayoutItemBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 |
12 | class AudioTransferCompleteLayoutItemViewHolder(
13 | private val audioTransferLayoutItemBinding: AudioTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(audioTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceAudio
18 | audioTransferLayoutItemBinding.run {
19 | this.dataToTransfer = dataToTransfer
20 |
21 | audioTransferLayoutItemShimmer.run {
22 | stopShimmer()
23 | hideShimmer()
24 | }
25 |
26 | Glide.with(audioTransferLayoutItemVideoPreviewImageView)
27 | .load(dataToTransfer.audioArtPath)
28 | .error(R.drawable.ic_icons8_music)
29 | .into(audioTransferLayoutItemVideoPreviewImageView)
30 |
31 | }
32 | }
33 |
34 | companion object {
35 | fun createViewHolder(parent: ViewGroup): AudioTransferCompleteLayoutItemViewHolder {
36 | val layoutBinding = DataBindingUtil.inflate(
37 | LayoutInflater.from(parent.context),
38 | R.layout.audio_transfer_layout_item,
39 | parent,
40 | false
41 | )
42 | return AudioTransferCompleteLayoutItemViewHolder(layoutBinding)
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/audio/AudioReceiveCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.receivedDataFragment.viewHolders.audio
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.AudioTransferLayoutItemBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 |
12 | class AudioReceiveCompleteLayoutItemViewHolder(
13 | private val audioTransferLayoutItemBinding: AudioTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(audioTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceAudio
18 | audioTransferLayoutItemBinding.run {
19 | this.dataToTransfer = dataToTransfer
20 |
21 | audioTransferLayoutItemShimmer.run {
22 | stopShimmer()
23 | hideShimmer()
24 | }
25 |
26 | Glide.with(audioTransferLayoutItemVideoPreviewImageView)
27 | .load(dataToTransfer.audioArtPath)
28 | .error(R.drawable.ic_icons8_music)
29 | .into(audioTransferLayoutItemVideoPreviewImageView)
30 |
31 | }
32 | }
33 |
34 | companion object {
35 | fun createViewHolder(parent: ViewGroup): AudioReceiveCompleteLayoutItemViewHolder {
36 | val layoutBinding = DataBindingUtil.inflate(
37 | LayoutInflater.from(parent.context),
38 | R.layout.audio_transfer_layout_item,
39 | parent,
40 | false
41 | )
42 | return AudioReceiveCompleteLayoutItemViewHolder(layoutBinding)
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
15 |
16 |
19 |
20 |
23 |
24 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
38 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/DataToTransferViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import com.salesground.zipbolt.model.DataToTransfer
7 | import com.salesground.zipbolt.utils.SingleLiveDataEventForUIState
8 |
9 | class DataToTransferViewModel : ViewModel() {
10 |
11 | private val _dropAllSelectedItem = MutableLiveData>()
12 | val dropAllSelectedItem: LiveData>
13 | get() = _dropAllSelectedItem
14 |
15 | private var _collectionOfDataToTransferLiveData = MutableLiveData>(
16 | mutableListOf()
17 | )
18 | val collectionOfDataToTransferLiveData: LiveData>
19 | get() = _collectionOfDataToTransferLiveData
20 |
21 | private var _collectionOfDataToTransfer: MutableList = mutableListOf()
22 | val collectionOfDataToTransfer: MutableList
23 | get() = _collectionOfDataToTransfer
24 |
25 | fun addDataToTransfer(dataToTransfer: DataToTransfer) {
26 | collectionOfDataToTransfer.add(dataToTransfer)
27 | _collectionOfDataToTransferLiveData.value = collectionOfDataToTransfer
28 | }
29 |
30 | fun removeDataFromDataToTransfer(dataToTransfer: DataToTransfer) {
31 | collectionOfDataToTransfer.remove(dataToTransfer)
32 | _collectionOfDataToTransferLiveData.value = collectionOfDataToTransfer
33 | }
34 |
35 | private fun clearCollectionOfDataToTransfer() {
36 | _collectionOfDataToTransfer = mutableListOf()
37 | _collectionOfDataToTransferLiveData.value = collectionOfDataToTransfer
38 | }
39 |
40 | fun dropAllSelectedItems() {
41 | clearCollectionOfDataToTransfer()
42 | _dropAllSelectedItem.value = SingleLiveDataEventForUIState(true)
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/video/VideoTransferCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.video
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.VideoTransferLayoutItemBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 |
12 | class VideoTransferCompleteLayoutItemViewHolder(
13 | private val videoTransferLayoutItemBinding: VideoTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(videoTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceVideo
18 | videoTransferLayoutItemBinding.run {
19 | videoSize = dataToTransfer.videoSize
20 | videoDuration = dataToTransfer.videoDuration
21 | videoName = dataToTransfer.videoDisplayName
22 |
23 | videoTransferLayoutItemShimmer.run {
24 | stopShimmer()
25 | hideShimmer()
26 | }
27 |
28 | Glide.with(videoTransferLayoutItemVideoPreviewImageView)
29 | .load(dataToTransfer.dataUri)
30 | .into(videoTransferLayoutItemVideoPreviewImageView)
31 | }
32 | }
33 |
34 | companion object {
35 | fun createViewHolder(parent: ViewGroup): VideoTransferCompleteLayoutItemViewHolder {
36 | val layoutBinding = DataBindingUtil.inflate(
37 | LayoutInflater.from(parent.context),
38 | R.layout.video_transfer_layout_item,
39 | parent,
40 | false
41 | )
42 | return VideoTransferCompleteLayoutItemViewHolder(layoutBinding)
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/video/VideoReceiveCompleteLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.ongoingDataTransferRecyclerViewComponents.viewHolders.video
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.VideoTransferLayoutItemBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 |
12 | class VideoReceiveCompleteLayoutItemViewHolder(
13 | private val videoTransferLayoutItemBinding: VideoTransferLayoutItemBinding
14 | ) : RecyclerView.ViewHolder(videoTransferLayoutItemBinding.root) {
15 |
16 | fun bindData(dataToTransfer: DataToTransfer) {
17 | dataToTransfer as DataToTransfer.DeviceVideo
18 | videoTransferLayoutItemBinding.run {
19 | videoSize = dataToTransfer.videoSize
20 | videoDuration = dataToTransfer.videoDuration
21 | videoName = dataToTransfer.videoDisplayName
22 |
23 | videoTransferLayoutItemShimmer.run {
24 | stopShimmer()
25 | hideShimmer()
26 | }
27 |
28 | Glide.with(videoTransferLayoutItemVideoPreviewImageView)
29 | .load(dataToTransfer.dataUri)
30 | .into(videoTransferLayoutItemVideoPreviewImageView)
31 | }
32 | }
33 |
34 | companion object {
35 | fun createViewHolder(parent: ViewGroup): VideoReceiveCompleteLayoutItemViewHolder {
36 | val layoutBinding = DataBindingUtil.inflate(
37 | LayoutInflater.from(parent.context),
38 | R.layout.video_transfer_layout_item,
39 | parent,
40 | false
41 | )
42 | return VideoReceiveCompleteLayoutItemViewHolder(layoutBinding)
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_dat_file_logo.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/folder_layout_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
19 |
20 |
26 |
27 |
28 |
36 |
37 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/text_file_icon.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
21 |
22 |
31 |
32 |
33 |
34 |
35 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/peersDiscoveryFragment/DiscoveredPeerLayoutItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.peersDiscoveryFragment
2 |
3 | import android.net.wifi.p2p.WifiP2pDevice
4 | import android.view.LayoutInflater
5 | import android.view.ViewGroup
6 | import androidx.databinding.DataBindingUtil
7 | import androidx.recyclerview.widget.RecyclerView
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.DiscoveredPeerLayoutItemBinding
10 | import com.salesground.zipbolt.ui.recyclerview.RecyclerViewItemClickedListener
11 |
12 | class DiscoveredPeerLayoutItemViewHolder(
13 | private val discoveredPeerLayoutItemBinding: DiscoveredPeerLayoutItemBinding,
14 | private val deviceClickedListener: RecyclerViewItemClickedListener
15 | ) : RecyclerView.ViewHolder(discoveredPeerLayoutItemBinding.root) {
16 |
17 | fun bindData(device: WifiP2pDevice) {
18 | discoveredPeerLayoutItemBinding.run {
19 | deviceName = if (device.deviceName.isNotBlank()) {
20 | device.deviceName
21 | } else {
22 | "Unspecified Device"
23 | }
24 | discoveredPeerLayoutItemViewGroup.setOnClickListener {
25 | deviceClickedListener.onClick(device)
26 | }
27 |
28 | executePendingBindings()
29 | }
30 | }
31 |
32 | companion object {
33 | fun createViewHolder(
34 | parent: ViewGroup,
35 | deviceClickedListener: RecyclerViewItemClickedListener
36 | ): DiscoveredPeerLayoutItemViewHolder {
37 | val layoutBinding = DataBindingUtil.inflate<
38 | DiscoveredPeerLayoutItemBinding>(
39 | LayoutInflater.from(parent.context),
40 | R.layout.discovered_peer_layout_item,
41 | parent,
42 | false
43 | )
44 | return DiscoveredPeerLayoutItemViewHolder(
45 | layoutBinding,
46 | deviceClickedListener
47 | )
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/application/ApplicationTransferWaitingViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.application
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.ApplicationLayoutItemTransferOrReceiveBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 | import com.salesground.zipbolt.utils.transformDataSizeToMeasuredUnit
12 |
13 | class ApplicationTransferWaitingViewHolder(
14 | private val applicationLayoutItemTransferOrReceiveBinding: ApplicationLayoutItemTransferOrReceiveBinding
15 | ) : RecyclerView.ViewHolder(applicationLayoutItemTransferOrReceiveBinding.root) {
16 |
17 | fun bindData(dataToTransfer: DataToTransfer) {
18 | dataToTransfer as DataToTransfer.DeviceApplication
19 |
20 | applicationLayoutItemTransferOrReceiveBinding.run {
21 | applicationName = dataToTransfer.dataDisplayName
22 | applicationSize = dataToTransfer.dataSize.transformDataSizeToMeasuredUnit()
23 |
24 | Glide.with(applicationLayoutItemTransferOrReceiveImageView)
25 | .load(dataToTransfer.applicationIcon)
26 | .into(applicationLayoutItemTransferOrReceiveImageView)
27 |
28 | executePendingBindings()
29 | }
30 | }
31 |
32 | companion object {
33 | fun createViewHolder(parent: ViewGroup): ApplicationTransferWaitingViewHolder {
34 | val layoutBinding =
35 | DataBindingUtil.inflate(
36 | LayoutInflater.from(parent.context),
37 | R.layout.application_layout_item_transfer_or_receive,
38 | parent,
39 | false
40 | )
41 | return ApplicationTransferWaitingViewHolder(layoutBinding)
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/expanded_bottom_sheet_layout_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
30 |
31 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/customviews/SelectableLinearLayout.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.customviews
2 |
3 | import android.animation.AnimatorSet
4 | import android.animation.ValueAnimator
5 | import android.content.Context
6 | import android.graphics.Canvas
7 | import android.graphics.CornerPathEffect
8 | import android.graphics.Paint
9 | import android.graphics.RectF
10 | import android.util.AttributeSet
11 | import androidx.appcompat.widget.LinearLayoutCompat
12 | import androidx.core.content.ContextCompat
13 | import com.google.android.material.animation.AnimatorSetCompat
14 | import com.salesground.zipbolt.R
15 |
16 | class SelectableLinearLayout @JvmOverloads constructor(
17 | context: Context, attrs: AttributeSet? = null
18 | ) : LinearLayoutCompat(context, attrs) {
19 | private var isViewSelected = false
20 | private val cornerRect = RectF()
21 | private val cornerRectFillPaint = Paint().apply {
22 | style = Paint.Style.FILL
23 | isAntiAlias = true
24 | isDither = true
25 | color = ContextCompat.getColor(context, R.color.blue_415_15_percent_alpha)
26 | }
27 |
28 | override fun dispatchDraw(canvas: Canvas?) {
29 | super.dispatchDraw(canvas)
30 | if (isViewSelected) {
31 | canvas?.let {
32 | drawScrim(it)
33 | }
34 | }
35 | }
36 |
37 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
38 | super.onSizeChanged(w, h, oldw, oldh)
39 | cornerRect.apply {
40 | left = 0f
41 | top = 0f
42 | right = w.toFloat()
43 | bottom = h.toFloat()
44 | }
45 | }
46 |
47 | private fun drawScrim(canvas: Canvas) {
48 | canvas.drawRect(
49 | cornerRect,
50 | cornerRectFillPaint
51 | )
52 | }
53 |
54 |
55 | fun setIsViewSelected(selected: Boolean) {
56 | if (isViewSelected && !selected) {
57 | isViewSelected = selected
58 | invalidate()
59 | } else if (!isViewSelected && selected) {
60 | isViewSelected = selected
61 | invalidate()
62 | }
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/wifi_p2p_device_item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
10 |
11 |
14 |
15 |
28 |
29 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/salesground/zipbolt/repository/DeviceApplicationsRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | import com.salesground.zipbolt.repository.implementation.DeviceApplicationsRepository
2 |
3 |
4 | import android.content.Context
5 | import android.content.pm.ApplicationInfo
6 | import android.os.Build
7 | import androidx.test.core.app.ApplicationProvider
8 | import com.salesground.zipbolt.repository.ZipBoltSavedFilesRepository
9 | import kotlinx.coroutines.flow.collect
10 | import kotlinx.coroutines.runBlocking
11 | import org.junit.Before
12 |
13 | import org.junit.Assert.*
14 | import org.junit.Test
15 |
16 | class DeviceApplicationsRepositoryTest {
17 |
18 | private val applicationContext = ApplicationProvider.getApplicationContext()
19 | private lateinit var deviceApplicationsRepository: DeviceApplicationsRepository
20 |
21 | @Before
22 | fun setUp() {
23 | deviceApplicationsRepository = DeviceApplicationsRepository(applicationContext,
24 | ZipBoltSavedFilesRepository())
25 | }
26 |
27 | // confirm that the mutable list allAppsOnDevice holds items
28 | @Test
29 | fun test_getAllAppsOnDevice_returnsAListOfApps() {
30 | runBlocking {
31 | val allAppsOnDevice = deviceApplicationsRepository.getAllAppsOnDevice()
32 | assertTrue(allAppsOnDevice.isNotEmpty())
33 | }
34 | }
35 |
36 | @Test
37 | fun test_getAllAppsOnDevice_returnsOnlyAppsInstalledByTheUserInAndroidVersionGreaterThan8() {
38 | runBlocking {
39 | val allAppsOnDevice = deviceApplicationsRepository.getAllAppsOnDevice()
40 |
41 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
42 | allAppsOnDevice.forEach {
43 | assertNotEquals(it.flags, ApplicationInfo.FLAG_SYSTEM)
44 | }
45 | }
46 | }
47 | }
48 |
49 | // confirm that all apps on device has an icon
50 | @Test
51 | fun test_thatAllAppsOnDeviceHasAnIcon() { runBlocking {
52 | val allAppsOnDevice = deviceApplicationsRepository.getAllAppsOnDevice()
53 |
54 | allAppsOnDevice.forEach {
55 | assertNotNull(it.loadIcon(applicationContext.packageManager))
56 | }
57 | }
58 | }
59 |
60 |
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
31 |
32 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/home_screen_recyclerview_layout_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
16 |
17 |
22 |
23 |
34 |
35 |
44 |
45 |
46 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/sentDataFragment/viewHolders/application/ApplicationTransferCompleteLayoutViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.sentDataFragment.viewHolders.application
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.ApplicationLayoutItemTransferOrReceiveBinding
10 | import com.salesground.zipbolt.model.DataToTransfer
11 | import com.salesground.zipbolt.utils.transformDataSizeToMeasuredUnit
12 |
13 | class ApplicationTransferCompleteLayoutViewHolder(
14 | private val applicationLayoutItemTransferOrReceiveBinding: ApplicationLayoutItemTransferOrReceiveBinding
15 | ) : RecyclerView.ViewHolder(applicationLayoutItemTransferOrReceiveBinding.root) {
16 |
17 | fun bindData(dataToTransfer: DataToTransfer) {
18 | dataToTransfer as DataToTransfer.DeviceApplication
19 |
20 | applicationLayoutItemTransferOrReceiveBinding.run {
21 | applicationName = dataToTransfer.dataDisplayName
22 | applicationSize = dataToTransfer.dataSize.transformDataSizeToMeasuredUnit()
23 |
24 | Glide.with(applicationLayoutItemTransferOrReceiveImageView)
25 | .load(dataToTransfer.applicationIcon)
26 | .into(applicationLayoutItemTransferOrReceiveImageView)
27 |
28 | applicationLayoutItemTransferOrReceiveShimmer.run {
29 | stopShimmer()
30 | hideShimmer()
31 | }
32 | executePendingBindings()
33 | }
34 | }
35 |
36 | companion object {
37 | fun createViewHolder(parent: ViewGroup): ApplicationTransferCompleteLayoutViewHolder {
38 | val layoutBinding =
39 | DataBindingUtil.inflate(
40 | LayoutInflater.from(parent.context),
41 | R.layout.application_layout_item_transfer_or_receive,
42 | parent,
43 | false
44 | )
45 | return ApplicationTransferCompleteLayoutViewHolder(layoutBinding)
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-v23/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
23 |
26 |
27 |
30 |
31 |
34 |
35 |
38 |
39 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/application_layout_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
33 |
34 |
45 |
46 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/recyclerview/receivedDataFragment/viewHolders/application/ApplicationReceiveCompleteLayoutViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.recyclerview.ongoingDataTransferRecyclerViewComponents.viewHolders.application
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.salesground.zipbolt.R
9 | import com.salesground.zipbolt.databinding.ApplicationLayoutItemTransferOrReceiveBinding
10 | import com.salesground.zipbolt.databinding.ApplicationReceiveCompleteLayoutItemBinding
11 | import com.salesground.zipbolt.model.DataToTransfer
12 | import com.salesground.zipbolt.ui.recyclerview.RecyclerViewItemClickedListener
13 | import com.salesground.zipbolt.utils.transformDataSizeToMeasuredUnit
14 |
15 | class ApplicationReceiveCompleteLayoutViewHolder(
16 | private val applicationReceiveCompleteLayoutItemBinding: ApplicationReceiveCompleteLayoutItemBinding
17 | ) : RecyclerView.ViewHolder(applicationReceiveCompleteLayoutItemBinding.root) {
18 |
19 | fun bindData(dataToTransfer: DataToTransfer) {
20 | dataToTransfer as DataToTransfer.DeviceApplication
21 |
22 | applicationReceiveCompleteLayoutItemBinding.run {
23 | applicationName = dataToTransfer.dataDisplayName
24 | applicationSize = dataToTransfer.dataSize.transformDataSizeToMeasuredUnit()
25 |
26 | Glide.with(applicationReceiveLayoutItemImageView)
27 | .load(dataToTransfer.applicationIcon)
28 | .into(applicationReceiveLayoutItemImageView)
29 | executePendingBindings()
30 | }
31 | }
32 |
33 | companion object {
34 | fun createViewHolder(
35 | parent: ViewGroup
36 | ): ApplicationReceiveCompleteLayoutViewHolder {
37 | val layoutBinding =
38 | DataBindingUtil.inflate(
39 | LayoutInflater.from(parent.context),
40 | R.layout.application_receive_complete_layout_item,
41 | parent,
42 | false
43 | )
44 | return ApplicationReceiveCompleteLayoutViewHolder(
45 | layoutBinding
46 | )
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/salesground/zipbolt/repository/ZipBoltImageRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.repository
2 |
3 | import android.content.Context
4 | import android.graphics.drawable.Drawable
5 | import android.os.ParcelFileDescriptor
6 | import android.util.Log
7 | import androidx.test.core.app.ApplicationProvider
8 | import com.bumptech.glide.Glide
9 | import com.salesground.zipbolt.model.DataToTransfer
10 | import com.salesground.zipbolt.repository.implementation.ZipBoltImageRepository
11 | import dagger.hilt.android.testing.HiltAndroidRule
12 | import dagger.hilt.android.testing.HiltAndroidTest
13 | import junit.framework.Assert.assertEquals
14 | import org.junit.Before
15 | import org.junit.Rule
16 | import org.junit.Test
17 | import javax.inject.Inject
18 | import kotlinx.coroutines.*
19 | import java.io.DataInputStream
20 | import java.io.FileInputStream
21 |
22 | @HiltAndroidTest
23 | class ZipBoltImageRepositoryTest {
24 |
25 | @get:Rule
26 | var hiltRule = HiltAndroidRule(this)
27 |
28 | @Inject
29 | lateinit var zipBoltImageRepository: ZipBoltImageRepository
30 |
31 | private val context = ApplicationProvider.getApplicationContext()
32 |
33 | @Before
34 | fun setUp() {
35 | hiltRule.inject()
36 | }
37 |
38 | @Test
39 | fun test_getImagesOnDevice() = runBlocking {
40 | val allImagesOnDevice = zipBoltImageRepository.getImagesOnDevice()
41 | assert(allImagesOnDevice.isNotEmpty())
42 | val deviceImage = allImagesOnDevice[1] as DataToTransfer.DeviceImage
43 | assert(deviceImage.imageBucketName.isNotBlank())
44 | val firstTenImagesOnDevice = zipBoltImageRepository.getImagesOnDevice(limit = 10)
45 | assert(firstTenImagesOnDevice.size <= 10)
46 | }
47 |
48 | @Test
49 | fun test_getMetaDataOfImage() = runBlocking {
50 | var firstImage = zipBoltImageRepository.getImagesOnDevice().first() as DataToTransfer.DeviceImage
51 | assert(firstImage.imageMimeType.contains("image"))
52 | assert(firstImage.imageSize > 10)
53 | assert(firstImage.imageDisplayName != "")
54 | }
55 |
56 | @Test
57 | fun test_imageUriHoldsADrawable() = runBlocking {
58 | var firstImage = zipBoltImageRepository.getImagesOnDevice().first() as DataToTransfer.DeviceImage
59 | assert(Glide.with(context).load(firstImage.imageUri).submit().get() != null)
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/customviews/AnimatedLoadingTextView.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.customviews
2 |
3 | import android.animation.ValueAnimator
4 | import android.animation.ValueAnimator.*
5 | import android.content.Context
6 | import android.graphics.Canvas
7 | import android.util.AttributeSet
8 | import android.util.Log
9 | import android.view.View
10 | import com.google.android.material.textview.MaterialTextView
11 |
12 | class AnimatedLoadingTextView @JvmOverloads constructor(
13 | context: Context, attrs: AttributeSet? = null
14 | ) : MaterialTextView(context, attrs) {
15 | private var initialTextSize: Int = 0
16 | private var initialText: String = text.toString()
17 | private var threeDots: String = "$initialText..."
18 | private var twoDots: String = "$initialText.."
19 | private var oneDot: String = "$initialText."
20 | private var textAnimator: ValueAnimator? = null
21 |
22 | init {
23 | animateText()
24 | if (visibility == View.INVISIBLE) {
25 | textAnimator?.cancel()
26 | }
27 | }
28 |
29 | override fun onDraw(canvas: Canvas?) {
30 | super.onDraw(canvas)
31 | }
32 |
33 | override fun onVisibilityChanged(changedView: View, visibility: Int) {
34 | super.onVisibilityChanged(changedView, visibility)
35 | if (visibility != View.INVISIBLE) {
36 | textAnimator?.start()
37 | }
38 | }
39 |
40 | fun setAnimatedText(newText: String) {
41 | initialText = newText
42 | threeDots = "$initialText..."
43 | twoDots = "$initialText.."
44 | oneDot = "$initialText."
45 | }
46 |
47 |
48 | private fun animateText() {
49 | initialTextSize = text.length
50 | textAnimator = ofInt(0, 4).apply {
51 | repeatMode = REVERSE
52 | repeatCount = INFINITE
53 | duration = 2500
54 | start()
55 | }
56 | textAnimator?.addUpdateListener {
57 | text = when (it.animatedValue) {
58 | 0 -> {
59 | initialText
60 | }
61 | 1 -> {
62 | oneDot
63 | }
64 | 2 -> {
65 | twoDots
66 | }
67 | 3 -> {
68 | threeDots
69 | }
70 | else -> {
71 | threeDots
72 | }
73 | }
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/multi_colored_apple_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/communication/ZipBoltMTP.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.communication
2 |
3 | import android.content.Context
4 | import android.os.ParcelFileDescriptor
5 | import com.salesground.zipbolt.model.MediaModel
6 | import com.salesground.zipbolt.repository.ZipBoltSavedFilesRepository
7 | import dagger.hilt.android.qualifiers.ApplicationContext
8 | import java.io.*
9 | import javax.inject.Inject
10 |
11 | class ZipBoltMTP @Inject constructor (
12 | @ApplicationContext private val context: Context,
13 | private val zipBoltSavedFilesRepository: ZipBoltSavedFilesRepository) {
14 |
15 | fun transferMedia(mediaItems: MutableList, DOS: DataOutputStream) {
16 | DOS.writeInt(mediaItems.size)
17 | mediaItems.forEach { mediaModel: MediaModel ->
18 | DOS.writeUTF(mediaModel.mediaDisplayName)
19 | DOS.writeLong(mediaModel.mediaSize)
20 | DOS.writeUTF(mediaModel.mimeType)
21 | context.contentResolver.openFileDescriptor(mediaModel.mediaUri, "r").apply {
22 | this?.let { parcelFileDescriptor: ParcelFileDescriptor ->
23 | val mediaModelFileInputStream =
24 | FileInputStream(parcelFileDescriptor.fileDescriptor)
25 | val bufferArray = ByteArray(10_000_000)
26 | var lengthRead: Int
27 |
28 | while (mediaModelFileInputStream.read(bufferArray)
29 | .also { lengthRead = it } > 0
30 | ) {
31 | DOS.write(bufferArray, 0, lengthRead)
32 | }
33 | mediaModelFileInputStream.close()
34 | }
35 | }
36 |
37 | }
38 | }
39 |
40 | fun receiveMedia(DIS: DataInputStream) {
41 | val numberOfItemsSent = DIS.readInt()
42 | // Log.i("NewTransfer", "Received $numberOfItemsSent images")
43 | for (i in 0 until numberOfItemsSent) {
44 | val mediaName = DIS.readUTF()
45 | var mediaSize = DIS.readLong()
46 | val mediaType = DIS.readUTF()
47 |
48 | // read media bytes and save it into the media store based on the mime type
49 | when {
50 | mediaType.contains("image" , true) -> {
51 |
52 | }
53 | mediaType.contains("video", true) -> {
54 |
55 | }
56 | mediaType.contains("audio", true) -> {
57 |
58 | }
59 | }
60 |
61 | }
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/customviews/DividerLabel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.customviews
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.Paint
7 | import android.graphics.Rect
8 | import android.os.Build
9 | import android.util.AttributeSet
10 | import android.util.Log
11 | import androidx.annotation.RequiresApi
12 | import androidx.compose.ui.geometry.Offset
13 | import com.google.android.material.textview.MaterialTextView
14 | import com.salesground.zipbolt.R
15 |
16 | class DividerLabel @JvmOverloads constructor(
17 | context: Context, attrs: AttributeSet? = null
18 | ) : MaterialTextView(context, attrs) {
19 |
20 | private val dividerPaint: Paint
21 |
22 | init {
23 | context.theme.obtainStyledAttributes(R.styleable.DividerLabel).apply {
24 | dividerPaint = Paint().apply {
25 | style = Paint.Style.STROKE
26 | isAntiAlias = true
27 | isDither = true
28 | color = textColors.defaultColor
29 | strokeWidth = getFloat(
30 | R.styleable.DividerLabel_strokeLineHeight,
31 | 1f
32 | ) * context.resources.displayMetrics.scaledDensity
33 | strokeCap = Paint.Cap.ROUND
34 | strokeJoin = Paint.Join.ROUND
35 | }
36 | recycle()
37 | }
38 | textAlignment = TEXT_ALIGNMENT_CENTER
39 |
40 | }
41 |
42 |
43 | override fun onDraw(canvas: Canvas?) {
44 | super.onDraw(canvas)
45 | canvas?.let {
46 | drawDividerLine(
47 | canvas = it,
48 | viewHeight = measuredHeight,
49 | viewWidth = measuredWidth,
50 | writtenTextSize = paint.measureText(text.toString())
51 | )
52 | }
53 | }
54 |
55 | private fun drawDividerLine(
56 | canvas: Canvas,
57 | viewHeight: Int, viewWidth: Int,
58 | writtenTextSize: Float,
59 | ) {
60 |
61 | canvas.drawLine(
62 | paddingLeft.toFloat(),
63 | viewHeight * 0.5f,
64 | (viewWidth * 0.5f) - writtenTextSize,
65 | viewHeight * 0.5f,
66 | dividerPaint
67 | )
68 | canvas.drawLine(
69 | (viewWidth * 0.5f) + writtenTextSize,
70 | viewHeight * 0.5f, (viewWidth - paddingRight).toFloat(),
71 | viewHeight * 0.5f,
72 | dividerPaint
73 | )
74 | }
75 |
76 | }
--------------------------------------------------------------------------------
/app/src/main/res/font/work_sans_font_family.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
23 |
24 |
28 |
32 |
36 |
40 |
44 |
48 |
52 |
56 |
60 |
64 |
68 |
72 |
76 |
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/animationutils/AnimationUtils.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.animationutils
2 |
3 | import android.animation.AnimatorSet
4 | import android.animation.ValueAnimator
5 | import android.graphics.drawable.ColorDrawable
6 | import android.view.animation.AccelerateDecelerateInterpolator
7 | import android.widget.FrameLayout
8 | import android.widget.ImageView
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.graphics.toArgb
11 |
12 |
13 | fun ImageView.scaleUpAnimation(
14 | parent: FrameLayout,
15 | currentScaleValue: Float = 0.7f,
16 | currentParentForegroundColor: Int = Color.Blue.copy(0.12f).toArgb(),
17 | animationDuration: Long = 300
18 | ) {
19 | val scaleUpAnimation = ValueAnimator.ofFloat(currentScaleValue, 1f).apply {
20 | duration = animationDuration
21 | interpolator = AccelerateDecelerateInterpolator()
22 | }
23 | scaleUpAnimation.addUpdateListener {
24 | val currentScale = it.animatedValue as Float
25 | scaleX = currentScale
26 | scaleY = currentScale
27 | }
28 | val viewGroupColorChangeAnimation =
29 | ValueAnimator.ofArgb(currentParentForegroundColor, Color.Transparent.toArgb()).apply {
30 | duration = animationDuration
31 | }
32 | viewGroupColorChangeAnimation.addUpdateListener {
33 | parent.foreground = ColorDrawable(it.animatedValue as Int)
34 | }
35 |
36 | AnimatorSet().apply {
37 | playTogether(scaleUpAnimation, viewGroupColorChangeAnimation)
38 | start()
39 | }
40 |
41 | }
42 |
43 | fun ImageView.scaleDownAnimation(
44 | parent: FrameLayout,
45 | targetScaleValue: Float = 0.7f,
46 | targetParentForegroundValue: Int = Color.Blue.copy(0.12f).toArgb(),
47 | animationDuration: Long = 300
48 | ) {
49 | val scaleDownAnimation = ValueAnimator.ofFloat(1f, targetScaleValue).apply {
50 | duration = animationDuration
51 | interpolator = AccelerateDecelerateInterpolator()
52 | }
53 | scaleDownAnimation.addUpdateListener {
54 | val currentScale = it.animatedValue as Float
55 | scaleX = currentScale
56 | scaleY = currentScale
57 | }
58 | val viewGroupColorChangeAnimation =
59 | ValueAnimator.ofArgb(Color.Transparent.toArgb(), targetParentForegroundValue).apply {
60 | duration = animationDuration
61 | }
62 | viewGroupColorChangeAnimation.addUpdateListener {
63 | parent.foreground = ColorDrawable(it.animatedValue as Int)
64 | }
65 | AnimatorSet().apply {
66 | playTogether(scaleDownAnimation, viewGroupColorChangeAnimation)
67 | start()
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/utils/TimeUtils.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.utils
2 |
3 | import android.content.Context
4 | import android.database.Cursor
5 | import android.net.Uri
6 | import android.provider.MediaStore
7 | import java.text.DateFormat
8 | import java.text.SimpleDateFormat
9 | import java.util.*
10 |
11 | val dateFormat = SimpleDateFormat("d MMMM, yyyy", Locale.UK)
12 |
13 | fun Long.parseDate(): String {
14 | return dateFormat.format(this).customizeDate()
15 | }
16 |
17 | private fun String.splitDate(): List {
18 | return split(" ", ignoreCase = true)
19 | }
20 |
21 | private fun String.customizeDate(): String {
22 | var day: String = this
23 | val splitDate = splitDate()
24 | val presentDate = dateFormat.format(System.currentTimeMillis()).splitDate()
25 | // check if the month and year is the same
26 | if (splitDate[1] == presentDate[1] && splitDate[2] == presentDate[2]) {
27 | when (presentDate[0].toInt() - splitDate[0].toInt()) {
28 | 0 -> day = "Today"
29 | 1 -> day = "Yesterday"
30 | 2 -> day = "Two days ago"
31 | }
32 | }
33 | return day
34 | }
35 |
36 | fun Long.formatVideoDurationToString(): String {
37 | val durationInSeconds = this / 1000
38 | val durationInMinutes = durationInSeconds / 60
39 | val remainingSeconds = durationInSeconds % 60
40 |
41 | return if (remainingSeconds > 0 && durationInMinutes > 0) {
42 | if (remainingSeconds < 30) {
43 | "${durationInMinutes}min"
44 | } else {
45 | "${durationInMinutes + 1}min"
46 | }
47 | } else if (durationInMinutes == 0L) {
48 | "${durationInSeconds}sec"
49 | } else {
50 | "${durationInMinutes}min"
51 | }
52 | }
53 |
54 | fun Uri.getVideoDuration(context: Context): Long {
55 | var videoDuration = 0L
56 | context.contentResolver.query(
57 | this,
58 | arrayOf(MediaStore.Video.Media.DURATION),
59 | null,
60 | null,
61 | null
62 | )?.let { cursor: Cursor ->
63 | cursor.moveToFirst()
64 | videoDuration = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DURATION))
65 | }
66 |
67 | return videoDuration
68 | }
69 |
70 | fun Uri.getAudioDuration(context: Context): Long {
71 | var audioDuration = 0L
72 | context.contentResolver.query(
73 | this,
74 | arrayOf(MediaStore.Audio.Media.DURATION),
75 | null, null, null
76 | )?.let { cursor: Cursor ->
77 | cursor.moveToFirst()
78 | audioDuration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION))
79 | }
80 | return audioDuration
81 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/zip_bolt_send_file_header_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
14 |
15 |
16 |
23 |
24 |
33 |
34 |
46 |
47 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/viewmodel/ReceivedDataViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.viewmodel
2 |
3 | import android.net.Uri
4 | import androidx.lifecycle.LiveData
5 | import androidx.lifecycle.MutableLiveData
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.viewModelScope
8 | import com.salesground.zipbolt.model.DataToTransfer
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.launch
11 | import java.io.File
12 |
13 | class ReceivedDataViewModel : ViewModel() {
14 |
15 | var currentReceiveDataToTransferItem: DataToTransfer = DataToTransfer.DeviceFile(
16 | File(
17 | ""
18 | )
19 | )
20 | private val receivedDataItemsNormalList: MutableList = mutableListOf()
21 | private val _receivedDataItems = MutableLiveData>(mutableListOf())
22 | val receivedDataItems: LiveData>
23 | get() = _receivedDataItems
24 |
25 | private val _newReceivedItemPosition = MutableLiveData(-1)
26 | val newReceivedItemPosition: LiveData
27 | get() = _newReceivedItemPosition
28 |
29 | private val _ongoingDataReceivePercent = MutableLiveData(0f)
30 | val ongoingDataReceivePercent: LiveData
31 | get() = _ongoingDataReceivePercent
32 |
33 | private val _completedReceivedDataItem = MutableLiveData(null)
34 | val completedReceivedDataItem: LiveData
35 | get() = _completedReceivedDataItem
36 |
37 | private val _dataReceiveStartedDataItem = MutableLiveData(null)
38 | val dataReceiveStartedDataItem: LiveData
39 | get() = _dataReceiveStartedDataItem
40 |
41 | fun addDataToReceivedItems(dataToTransfer: DataToTransfer) {
42 | receivedDataItemsNormalList.add(dataToTransfer)
43 | viewModelScope.launch(Dispatchers.Main) {
44 | _completedReceivedDataItem.value = dataToTransfer
45 | _receivedDataItems.value = receivedDataItemsNormalList
46 | _newReceivedItemPosition.value = receivedDataItemsNormalList.size
47 | }
48 | }
49 |
50 | fun updateOngoingReceiveDataItemReceivePercent(receivePercent: Float) {
51 | viewModelScope.launch(Dispatchers.Main) {
52 | _ongoingDataReceivePercent.value = receivePercent
53 | }
54 | }
55 |
56 | fun onDataReceiveStarted(receivedDataItem: ReceivedDataItem) {
57 | viewModelScope.launch(Dispatchers.Main) {
58 | _dataReceiveStartedDataItem.value = receivedDataItem
59 | }
60 | }
61 | }
62 |
63 | data class ReceivedDataItem(
64 | val dataDisplayName: String,
65 | val dataSize: Long,
66 | val percentageOfDataRead: Float,
67 | val dataType: Int,
68 | val dataUri: Uri?
69 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/salesground/zipbolt/ui/customviews/SelectableConstraintLayout.kt:
--------------------------------------------------------------------------------
1 | package com.salesground.zipbolt.ui.customviews
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.CornerPathEffect
6 | import android.graphics.Paint
7 | import android.graphics.RectF
8 | import android.util.AttributeSet
9 | import androidx.appcompat.widget.LinearLayoutCompat
10 | import androidx.constraintlayout.widget.ConstraintLayout
11 | import androidx.core.content.ContextCompat
12 | import com.salesground.zipbolt.R
13 |
14 | class SelectableConstraintLayout @JvmOverloads constructor(
15 | context: Context, attrs: AttributeSet? = null
16 | ) : ConstraintLayout(context, attrs) {
17 | private var isViewSelected = false
18 | private val cornerRect = RectF()
19 | private val cornerRectRadius = 4 * resources.displayMetrics.density
20 | private val cornerRectStrokePaint = Paint().apply {
21 | style = Paint.Style.STROKE
22 | isAntiAlias = true
23 | isDither = true
24 | color = ContextCompat.getColor(context, R.color.blue_415)
25 | strokeWidth = 4 * resources.displayMetrics.density
26 | pathEffect = CornerPathEffect(cornerRectRadius)
27 | strokeJoin = Paint.Join.ROUND
28 | strokeCap = Paint.Cap.ROUND
29 | }
30 |
31 | private val cornerRectFillPaint = Paint().apply {
32 | style = Paint.Style.FILL
33 | isAntiAlias = true
34 | isDither = true
35 | color = ContextCompat.getColor(context, R.color.blue_415_15_percent_alpha)
36 |
37 | }
38 |
39 | override fun dispatchDraw(canvas: Canvas?) {
40 | super.dispatchDraw(canvas)
41 | if (isViewSelected) {
42 | canvas?.let {
43 | drawScrim(it)
44 | drawRoundedCorners(it)
45 | }
46 | }
47 | }
48 |
49 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
50 | super.onSizeChanged(w, h, oldw, oldh)
51 | cornerRect.apply {
52 | left = 0f
53 | top = 0f
54 | right = measuredWidth.toFloat()
55 | bottom = measuredHeight.toFloat()
56 | }
57 | }
58 |
59 | private fun drawScrim(canvas: Canvas) {
60 | canvas.drawRect(
61 | cornerRect,
62 | cornerRectFillPaint
63 | )
64 | }
65 |
66 | private fun drawRoundedCorners(canvas: Canvas) {
67 | canvas.drawRect(
68 | cornerRect,
69 | cornerRectStrokePaint
70 | )
71 | }
72 |
73 | fun setIsViewSelected(selected: Boolean) {
74 | if (isViewSelected && !selected) {
75 | isViewSelected = selected
76 | invalidate()
77 | } else if (!isViewSelected && selected) {
78 | isViewSelected = selected
79 | invalidate()
80 | }
81 | }
82 |
83 | }
--------------------------------------------------------------------------------