Repository: jinkg/Style
Branch: master
Commit: 05796d467dae
Files: 342
Total size: 868.2 KB
Directory structure:
gitextract_vzylt4jg/
├── .gitignore
├── README.md
├── build.gradle
├── buildApk.bat
├── buildApk.sh
├── data/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── yalin/
│ │ └── data/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── cpp/
│ │ │ └── facet_id-lib.cpp
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── yalin/
│ │ │ └── style/
│ │ │ └── data/
│ │ │ ├── SyncConfig.java
│ │ │ ├── cache/
│ │ │ │ ├── AdvanceWallpaperCache.kt
│ │ │ │ ├── AdvanceWallpaperCacheImpl.kt
│ │ │ │ ├── GalleryWallpaperCache.kt
│ │ │ │ ├── SourcesCache.kt
│ │ │ │ ├── SourcesCacheImpl.kt
│ │ │ │ ├── WallpaperCache.java
│ │ │ │ └── WallpaperCacheImpl.java
│ │ │ ├── entity/
│ │ │ │ ├── AdvanceWallpaperEntity.java
│ │ │ │ ├── DeviceInfo.java
│ │ │ │ ├── GalleryWallpaperEntity.kt
│ │ │ │ ├── HttpRequestBody.java
│ │ │ │ ├── SourceEntity.kt
│ │ │ │ ├── WallpaperEntity.java
│ │ │ │ └── mapper/
│ │ │ │ ├── AdvanceWallpaperEntityMapper.java
│ │ │ │ └── WallpaperEntityMapper.java
│ │ │ ├── exception/
│ │ │ │ ├── LikeException.java
│ │ │ │ ├── NetworkConnectionException.java
│ │ │ │ ├── NoContentException.java
│ │ │ │ ├── RemoteServerException.java
│ │ │ │ └── ReswitchException.java
│ │ │ ├── executor/
│ │ │ │ ├── JobExecutor.java
│ │ │ │ └── SerialJobExecutor.java
│ │ │ ├── extensions/
│ │ │ │ └── DelegateExt.kt
│ │ │ ├── lock/
│ │ │ │ ├── LikeWallpaperLock.java
│ │ │ │ ├── OpenInputStreamLock.java
│ │ │ │ ├── ResourceLock.java
│ │ │ │ └── SelectSourceLock.java
│ │ │ ├── log/
│ │ │ │ └── LogUtil.java
│ │ │ ├── observable/
│ │ │ │ ├── SourcesObservableImpl.kt
│ │ │ │ └── WallpaperObservableImpl.kt
│ │ │ ├── repository/
│ │ │ │ ├── AdvanceWallpaperDataRepository.kt
│ │ │ │ ├── GalleryWallpaperDataRepository.kt
│ │ │ │ ├── SourcesDataRepository.kt
│ │ │ │ ├── StyleWallpaperDataRepository.java
│ │ │ │ └── datasource/
│ │ │ │ ├── AdvanceWallpaperDataStore.kt
│ │ │ │ ├── AdvanceWallpaperDataStoreFactory.kt
│ │ │ │ ├── AdvanceWallpaperDataStoreImpl.kt
│ │ │ │ ├── CacheWallpaperDataStore.java
│ │ │ │ ├── DbWallpaperDataStore.java
│ │ │ │ ├── GalleryWallpaperDataStore.kt
│ │ │ │ ├── GalleryWallpaperDataStoreFactory.kt
│ │ │ │ ├── RemoteAdvanceWallpaperDataStore.kt
│ │ │ │ ├── SourcesDataStore.kt
│ │ │ │ ├── SourcesDataStoreFactory.kt
│ │ │ │ ├── SourcesDataStoreImpl.kt
│ │ │ │ ├── StyleWallpaperDataStoreFactory.java
│ │ │ │ ├── WallpaperDataStore.java
│ │ │ │ ├── io/
│ │ │ │ │ ├── AdvanceWallpaperHandler.kt
│ │ │ │ │ ├── GalleryWallpapersHandler.kt
│ │ │ │ │ ├── JSONHandler.java
│ │ │ │ │ ├── RemoveGalleryWallpapersHandler.kt
│ │ │ │ │ └── WallpapersHandler.java
│ │ │ │ ├── net/
│ │ │ │ │ ├── DataFetcher.java
│ │ │ │ │ └── RemoteAdvanceWallpaperFetcher.java
│ │ │ │ ├── provider/
│ │ │ │ │ ├── StyleContract.java
│ │ │ │ │ ├── StyleContractHelper.java
│ │ │ │ │ ├── StyleDatabase.java
│ │ │ │ │ ├── StyleProvider.java
│ │ │ │ │ ├── StyleProviderUriMatcher.java
│ │ │ │ │ └── StyleUriEnum.java
│ │ │ │ └── sync/
│ │ │ │ ├── RemoteStyleDataFetcher.java
│ │ │ │ ├── StyleDataHandler.java
│ │ │ │ ├── SyncAdapter.java
│ │ │ │ ├── SyncHelper.java
│ │ │ │ ├── SyncService.java
│ │ │ │ ├── account/
│ │ │ │ │ ├── Account.java
│ │ │ │ │ ├── Authenticator.java
│ │ │ │ │ └── AuthenticatorService.java
│ │ │ │ └── gallery/
│ │ │ │ └── GalleryScheduleService.kt
│ │ │ └── utils/
│ │ │ ├── ChecksumUtil.java
│ │ │ ├── DeviceUtil.java
│ │ │ ├── FacetIdUtil.java
│ │ │ ├── HttpRequestUtil.java
│ │ │ ├── NativeFileHelper.kt
│ │ │ ├── NetworkUtil.java
│ │ │ ├── SelectionBuilder.java
│ │ │ ├── TimeUtil.java
│ │ │ ├── UriUtil.kt
│ │ │ ├── WallpaperFileHelper.java
│ │ │ └── WallpaperUtil.kt
│ │ └── res/
│ │ ├── values/
│ │ │ └── strings.xml
│ │ ├── values-zh-rCN/
│ │ │ └── strings.xml
│ │ └── xml/
│ │ ├── authenticator.xml
│ │ └── syncadapter.xml
│ └── test/
│ └── java/
│ └── com/
│ └── yalin/
│ └── data/
│ └── ExampleUnitTest.java
├── domain/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── yalin/
│ └── style/
│ └── domain/
│ ├── AdvanceWallpaper.java
│ ├── GalleryWallpaper.java
│ ├── Source.java
│ ├── Wallpaper.java
│ ├── exception/
│ │ ├── DefaultErrorBundle.java
│ │ └── ErrorBundle.java
│ ├── executor/
│ │ ├── PostExecutionThread.java
│ │ ├── SerialThreadExecutor.java
│ │ └── ThreadExecutor.java
│ ├── interactor/
│ │ ├── AddGalleryWallpaper.java
│ │ ├── DefaultObserver.java
│ │ ├── DownloadAdvanceWallpaper.java
│ │ ├── ForceNow.java
│ │ ├── GetAdvanceWallpapers.java
│ │ ├── GetGalleryUpdateInterval.java
│ │ ├── GetGalleryWallpaper.java
│ │ ├── GetSelectedAdvanceWallpaper.java
│ │ ├── GetSelectedSource.java
│ │ ├── GetSources.java
│ │ ├── GetWallpaper.java
│ │ ├── GetWallpaperCount.java
│ │ ├── LikeWallpaper.java
│ │ ├── LoadAdvanceWallpaper.java
│ │ ├── ObserverSources.java
│ │ ├── ObserverWallpaper.java
│ │ ├── OpenWallpaperInputStream.java
│ │ ├── ReadAdvanceAd.java
│ │ ├── RemoveGalleryWallpaper.java
│ │ ├── SelectAdvanceWallpaper.java
│ │ ├── SelectSource.java
│ │ ├── SetGalleryUpdateInterval.java
│ │ ├── SwitchWallpaper.java
│ │ └── UseCase.java
│ ├── observable/
│ │ ├── SourcesObservable.java
│ │ └── WallpaperObservable.java
│ └── repository/
│ ├── SourcesRepository.java
│ └── WallpaperRepository.java
├── engine/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── yalin/
│ │ └── style/
│ │ └── engine/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ └── yalin/
│ │ │ │ └── style/
│ │ │ │ └── engine/
│ │ │ │ ├── GDXWallpaperServiceProxy.kt
│ │ │ │ ├── GLWallpaperServiceProxy.kt
│ │ │ │ ├── IProvider.java
│ │ │ │ ├── ProxyApi.java
│ │ │ │ ├── ProxyProvider.kt
│ │ │ │ ├── WallpaperActiveCallback.kt
│ │ │ │ ├── WallpaperServiceProxy.kt
│ │ │ │ ├── advance/
│ │ │ │ │ ├── AnimationWallpaper.java
│ │ │ │ │ ├── BokehRainbowCircle.java
│ │ │ │ │ └── BokehRainbowWallpaper.java
│ │ │ │ ├── component/
│ │ │ │ │ ├── ComponentContext.java
│ │ │ │ │ └── StyleClassLoader.kt
│ │ │ │ └── resource/
│ │ │ │ ├── BrandUtil.java
│ │ │ │ ├── CompatResources.java
│ │ │ │ ├── ReflectUtil.java
│ │ │ │ └── ResourcesManager.java
│ │ │ └── net/
│ │ │ └── rbgrn/
│ │ │ └── android/
│ │ │ └── glwallpaperservice/
│ │ │ ├── BaseConfigChooser.java
│ │ │ └── GLWallpaperService.java
│ │ └── res/
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── yalin/
│ └── style/
│ └── engine/
│ └── ExampleUnitTest.java
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── presentation/
│ ├── .gitignore
│ ├── build.gradle
│ ├── component-proguard.pro
│ ├── google-services.json
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── yalin/
│ │ └── style/
│ │ ├── ExampleInstrumentedTest.java
│ │ ├── provider/
│ │ │ └── DatabaseTest.java
│ │ └── sync/
│ │ └── SyncAdapterTest.java
│ ├── demo/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── yalin/
│ │ │ └── style/
│ │ │ └── util/
│ │ │ └── AdUtil.kt
│ │ └── res/
│ │ └── values/
│ │ └── ad_strings.xml
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── yalin/
│ │ │ └── style/
│ │ │ ├── LockScreenVisibleReceiver.kt
│ │ │ ├── StyleApplication.kt
│ │ │ ├── StyleWallpaperService.kt
│ │ │ ├── StyleWallpaperServiceMirror.kt
│ │ │ ├── UIThread.kt
│ │ │ ├── WallpaperDetailViewport.kt
│ │ │ ├── analytics/
│ │ │ │ ├── Analytics.kt
│ │ │ │ ├── Event.kt
│ │ │ │ └── IAnalytics.kt
│ │ │ ├── engine/
│ │ │ │ └── StyleWallpaperProxy.kt
│ │ │ ├── event/
│ │ │ │ ├── MainContainerInsetsChangedEvent.java
│ │ │ │ ├── SeenTutorialEvent.java
│ │ │ │ ├── StyleWallpaperSizeChangedEvent.java
│ │ │ │ ├── SwitchWallpaperServiceEvent.java
│ │ │ │ ├── SwitchingPhotosStateChangedEvent.java
│ │ │ │ ├── SystemWallpaperSizeChangedEvent.java
│ │ │ │ ├── WallpaperActivateEvent.java
│ │ │ │ ├── WallpaperDetailOpenedEvent.java
│ │ │ │ └── WallpaperSwitchEvent.java
│ │ │ ├── exception/
│ │ │ │ └── ErrorMessageFactory.java
│ │ │ ├── extensions/
│ │ │ │ └── DelegatesExtensions.kt
│ │ │ ├── injection/
│ │ │ │ ├── HasComponent.kt
│ │ │ │ ├── PerActivity.kt
│ │ │ │ ├── component/
│ │ │ │ │ ├── ApplicationComponent.kt
│ │ │ │ │ ├── SourceComponent.kt
│ │ │ │ │ └── WallpaperComponent.kt
│ │ │ │ └── modules/
│ │ │ │ ├── ApplicationModule.kt
│ │ │ │ ├── SourceModule.kt
│ │ │ │ └── WallpaperModule.kt
│ │ │ ├── mapper/
│ │ │ │ ├── AdvanceWallpaperItemMapper.kt
│ │ │ │ └── WallpaperItemMapper.kt
│ │ │ ├── model/
│ │ │ │ ├── AdvanceWallpaperItem.java
│ │ │ │ ├── GalleryWallpaperItem.java
│ │ │ │ ├── SourceItem.kt
│ │ │ │ └── WallpaperItem.java
│ │ │ ├── presenter/
│ │ │ │ ├── AdvanceSettingPresenter.kt
│ │ │ │ ├── GallerySettingPresenter.kt
│ │ │ │ ├── Presenter.kt
│ │ │ │ ├── SettingsChooseSourcePresenter.kt
│ │ │ │ └── WallpaperDetailPresenter.kt
│ │ │ ├── render/
│ │ │ │ ├── BitmapRegionLoader.java
│ │ │ │ ├── DemoRenderController.kt
│ │ │ │ ├── GLColorOverlay.java
│ │ │ │ ├── GLPicture.java
│ │ │ │ ├── GLTextureView.java
│ │ │ │ ├── GLUtil.java
│ │ │ │ ├── ImageBlurrer.java
│ │ │ │ ├── ImageUtil.java
│ │ │ │ ├── RenderController.kt
│ │ │ │ ├── StyleBlurRenderer.java
│ │ │ │ └── TickingFloatAnimator.java
│ │ │ ├── settings/
│ │ │ │ └── Prefs.java
│ │ │ ├── util/
│ │ │ │ ├── ImageLoader.java
│ │ │ │ ├── MathUtil.java
│ │ │ │ ├── MultiSelectionController.kt
│ │ │ │ ├── ScrimUtil.java
│ │ │ │ ├── SettingsUtil.java
│ │ │ │ ├── ShareUtil.kt
│ │ │ │ ├── SvgPathParser.java
│ │ │ │ └── TypefaceUtil.java
│ │ │ └── view/
│ │ │ ├── AdvanceSettingView.kt
│ │ │ ├── GallerySettingView.kt
│ │ │ ├── LoadingDataView.kt
│ │ │ ├── SourceChooseView.kt
│ │ │ ├── WallpaperDetailView.kt
│ │ │ ├── activity/
│ │ │ │ ├── AboutActivity.kt
│ │ │ │ ├── AdvanceSettingActivity.kt
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── GallerySettingActivity.kt
│ │ │ │ ├── SettingsActivity.kt
│ │ │ │ └── StyleActivity.kt
│ │ │ ├── component/
│ │ │ │ ├── CircleProgressView.java
│ │ │ │ ├── DownloadingDialog.kt
│ │ │ │ ├── DrawInsetsFrameLayout.java
│ │ │ │ ├── GalleryEmptyStateGraphicView.kt
│ │ │ │ ├── ObservableHorizontalScrollView.kt
│ │ │ │ ├── PanScaleProxyView.java
│ │ │ │ ├── Scrollbar.kt
│ │ │ │ ├── ShadowDipsTextView.kt
│ │ │ │ ├── TintableImageButton.kt
│ │ │ │ └── Zoomer.kt
│ │ │ └── fragment/
│ │ │ ├── AnimatedStyleLogoFragment.kt
│ │ │ ├── BaseFragment.kt
│ │ │ ├── SettingsAdvanceFragment.kt
│ │ │ ├── SettingsChooseSourceFragment.kt
│ │ │ ├── StyleRenderFragment.java
│ │ │ ├── TutorialFragment.kt
│ │ │ └── WallpaperDetailFragment.kt
│ │ └── res/
│ │ ├── anim/
│ │ │ └── image_fade_in.xml
│ │ ├── anim-v21/
│ │ │ └── tutorial_icon_emanate_interpolator.xml
│ │ ├── animator/
│ │ │ ├── fade_in.xml
│ │ │ ├── fade_out.xml
│ │ │ ├── tutorial_icon_emanate_wave1.xml
│ │ │ ├── tutorial_icon_emanate_wave1_path.xml
│ │ │ ├── tutorial_icon_emanate_wave2.xml
│ │ │ ├── tutorial_icon_emanate_wave2_path.xml
│ │ │ └── tutorial_icon_overlay_state_list.xml
│ │ ├── color/
│ │ │ └── selector_skip_tint.xml
│ │ ├── drawable/
│ │ │ ├── gallery_ic_add.xml
│ │ │ ├── gallery_ic_add_folder.xml
│ │ │ ├── gallery_ic_add_photo.xml
│ │ │ ├── gallery_ic_folder.xml
│ │ │ ├── grey_selectable_item_background_circle.xml
│ │ │ ├── intro_background_protection.xml
│ │ │ ├── popup_background.xml
│ │ │ ├── scrubber_control_selector.xml
│ │ │ ├── scrubber_progress_blur_amount.xml
│ │ │ ├── scrubber_progress_dim_amount.xml
│ │ │ ├── scrubber_progress_grey_amount.xml
│ │ │ ├── scrubber_progress_horizontal.xml
│ │ │ ├── settings_source_item_image_overlay.xml
│ │ │ ├── tutorial_icon_on_overlay.xml
│ │ │ └── white_circle_button.xml
│ │ ├── drawable-v21/
│ │ │ ├── avd_tutorial_icon_emanate.xml
│ │ │ ├── grey_selectable_item_background_circle.xml
│ │ │ ├── settings_source_item_image_overlay.xml
│ │ │ ├── tutorial_icon_emanate.xml
│ │ │ └── tutorial_icon_on_overlay.xml
│ │ ├── layout/
│ │ │ ├── activity_about.xml
│ │ │ ├── activity_advance_setting.xml
│ │ │ ├── activity_gallery_setting.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_settings.xml
│ │ │ ├── advance_chosen_wallpaper_item.xml
│ │ │ ├── animated_logo_fragment.xml
│ │ │ ├── dialog_downloading.xml
│ │ │ ├── gallery_chosen_photo_item.xml
│ │ │ ├── gallery_chosen_photo_tree_item.xml
│ │ │ ├── layout_include_about_content.xml
│ │ │ ├── layout_include_active.xml
│ │ │ ├── layout_include_settings_content.xml
│ │ │ ├── layout_include_tutorial_content.xml
│ │ │ ├── layout_include_wallpaper_detail.xml
│ │ │ ├── layout_include_wallpaper_tutorial.xml
│ │ │ ├── layout_settings_choose_source.xml
│ │ │ ├── layout_style_settings.xml
│ │ │ ├── layout_wallpaper_detail.xml
│ │ │ ├── settings_ab_spinner_list_item.xml
│ │ │ ├── settings_ab_spinner_list_item_dropdown.xml
│ │ │ └── settings_choose_source_item.xml
│ │ ├── menu/
│ │ │ ├── advance_activity.xml
│ │ │ ├── gallery_activity.xml
│ │ │ ├── gallery_selection.xml
│ │ │ ├── menu_settings.xml
│ │ │ ├── menu_settings_advanced.xml
│ │ │ └── style_overflow.xml
│ │ ├── values/
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── ids.xml
│ │ │ ├── integers.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-v19/
│ │ │ └── styles.xml
│ │ ├── values-v21/
│ │ │ └── styles.xml
│ │ ├── values-w820dp/
│ │ │ └── dimens.xml
│ │ ├── values-zh-rCN/
│ │ │ └── strings.xml
│ │ └── xml/
│ │ ├── shortcuts.xml
│ │ └── wallpaper.xml
│ ├── production/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── yalin/
│ │ │ └── style/
│ │ │ └── util/
│ │ │ └── AdUtil.kt
│ │ └── res/
│ │ └── values/
│ │ └── ad_strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── yalin/
│ └── style/
│ └── ExampleUnitTest.java
├── settings.gradle
└── switch.properties
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
================================================
FILE: README.md
================================================
# Deprecated, Not maintain anymore.
# Style Live Wallpaper
A live wallpaper project for Android
====================
For developer
=========
Style offers a sdk that allows you to build your own live wallpaper.
**[Develop Doc](https://github.com/jinkg/style-develop-sdk)** • **[For more examples](https://github.com/jinkg/style-sdk)**








================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'com.google.gms:google-services:3.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: buildApk.bat
================================================
@echo off
echo Start Build Apk
call gradlew clean
call gradlew assembleProductionRelease -Dchannel=kinglloy
call gradlew assembleProductionRelease -Dchannel=yingyongbao
call gradlew assembleProductionRelease -Dchannel=360 -Dapp_name=Style艺术壁纸
call gradlew assembleProductionRelease -Dchannel=vivo
call gradlew assembleProductionRelease -Dchannel=flyme
call gradlew assembleProductionRelease -Dchannel=wandoujia
call gradlew assembleProductionRelease -Dchannel=baidu -Dapp_name=Style艺术壁纸
call gradlew assembleProductionRelease -Dchannel=google
call gradlew assembleProductionRelease -Dchannel=huawei -Dapp_name=Style艺术壁纸
echo Build Apk Complete
================================================
FILE: buildApk.sh
================================================
#!/usr/bin/env bash
echo "Start Build Apk"
./gradlew clean
./gradlew assembleProductionRelease -Dchannel=kinglloy
./gradlew assembleProductionRelease -Dchannel=yingyongbao
./gradlew assembleProductionRelease -Dchannel=360 -Dapp_name=Style艺术壁纸
./gradlew assembleProductionRelease -Dchannel=vivo
./gradlew assembleProductionRelease -Dchannel=flyme
./gradlew assembleProductionRelease -Dchannel=wandoujia
./gradlew assembleProductionRelease -Dchannel=baidu -Dapp_name=Style艺术壁纸
./gradlew assembleProductionRelease -Dchannel=google
./gradlew assembleProductionRelease -Dchannel=huawei -Dapp_name=Style艺术壁纸
echo "Build Apk Complete"
================================================
FILE: data/.gitignore
================================================
/build
================================================
FILE: data/CMakeLists.txt
================================================
# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
facet_id-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
# Associated headers in the same location as their source
# file are automatically included.
src/main/cpp/facet_id-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
facet_id-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
================================================
FILE: data/build.gradle
================================================
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.2.3'
}
}
apply plugin: 'com.android.library'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'kotlin-android'
Properties properties = new Properties()
properties.load(project.rootProject.file('switch.properties').newDataInputStream())
def ENABLE_EXTERNAL_LOG = properties.get('ENABLE_EXTERNAL_LOG')
def LOG_ENABLE = properties.get('LOG_ENABLE')
def CHANNEL = System.getProperty("channel", "default")
android {
compileSdkVersion COMPILE_SDK_VERSION as int
buildToolsVersion BUILD_TOOLS_VERSION as String
defaultConfig {
minSdkVersion MIN_SDK_VERSION as int
targetSdkVersion TARGET_SDK_VERSION as int
versionCode APP_VERSION_CODE as int
versionName APP_VERSION_NAME as String
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
buildConfigField("boolean", "LOG_ENABLE", "${LOG_ENABLE}")
buildConfigField("String", "CHANNEL", "\"${CHANNEL}\"")
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
publishNonDefault true
productFlavors {
demo {
buildConfigField("boolean", "DEMO_MODE", "true")
buildConfigField("boolean", "ENABLE_EXTERNAL_LOG", "true")
buildConfigField("String", "SERVER_WALLPAPER_ENDPOINT", "\"${DEMO_API_WALLPAPER_ENDPOINT}\"")
}
production {
buildConfigField("boolean", "DEMO_MODE", "false")
buildConfigField("boolean", "ENABLE_EXTERNAL_LOG", "${ENABLE_EXTERNAL_LOG}")
buildConfigField("String", "SERVER_WALLPAPER_ENDPOINT", "\"${PRODUCTION_API_WALLPAPER_ENDPOINT}\"")
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "com.android.support:appcompat-v7:${SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:exifinterface:${SUPPORT_LIBRARY_VERSION}"
compile "org.jetbrains.kotlin:kotlin-stdlib:${KOTLIN_VERSION}"
compile "org.jetbrains.anko:anko-common:${ANKO_VESION}"
compile "org.jetbrains.anko:anko-sqlite:${ANKO_VESION}"
compile "com.google.code.gson:gson:${GSON_VERSION}"
compile "com.squareup.okhttp3:okhttp:${OK_HTTP_VERSION}"
compile project(':domain')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
}
================================================
FILE: data/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/jinyalin/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific liked options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you liked the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: data/src/androidTest/java/com/yalin/data/ExampleInstrumentedTest.java
================================================
package com.yalin.data;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.yalin.data.test", appContext.getPackageName());
}
}
================================================
FILE: data/src/main/AndroidManifest.xml
================================================
================================================
FILE: data/src/main/cpp/facet_id-lib.cpp
================================================
#include
#include
#include
#define LOG_TAG ("facet_id_check")
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
int LOG_ENABLE = 0;
const char *LOG = "LOG";
int check_facet_id(JNIEnv *, jstring);
jstring get_facet_id(JNIEnv *, jobject, jint);
void printLog(const char *);
extern "C"
jboolean
Java_com_yalin_style_data_utils_FacetIdUtil_checkCurrentFacetId__Landroid_content_Context_2I(
JNIEnv *env, jclass, jobject context, jint uId) {
jstring facet_id = get_facet_id(env, context, uId);;
int check_result = check_facet_id(env, facet_id);
bool result = check_result == 0;
return (jboolean) result;
}
extern "C"
jstring
Java_com_yalin_style_data_utils_FacetIdUtil_getFacetId__Landroid_content_Context_2I(
JNIEnv *env, jclass, jobject context, jint uId) {
return get_facet_id(env, context, uId);
}
jstring get_facet_id(JNIEnv *env, jobject context, jint uId) {
jclass contextClazz = env->FindClass("android/content/Context");
jmethodID getPackageManagerMethodId = env->GetMethodID(contextClazz, "getPackageManager",
"()Landroid/content/pm/PackageManager;");
jobject packageManager = env->CallObjectMethod(context, getPackageManagerMethodId);
printLog("PackageManager obtained.");
jclass packageManagerClazz = env->FindClass("android/content/pm/PackageManager");
jmethodID getPackageForUidMethodId = env->GetMethodID(packageManagerClazz, "getPackagesForUid",
"(I)[Ljava/lang/String;");
jobjectArray packageNames = (jobjectArray) (jarray) env->CallObjectMethod(packageManager,
getPackageForUidMethodId,
uId);
jstring packageName = (jstring) env->GetObjectArrayElement(packageNames, 0);
printLog("packageName obtained.");
jmethodID getPackageInfoMethodId = env->GetMethodID(packageManagerClazz, "getPackageInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
jfieldID flag = env->GetStaticFieldID(packageManagerClazz, "GET_SIGNATURES", "I");
jint signaturesFlag = env->GetStaticIntField(packageManagerClazz, flag);
jobject packageInfo = env->CallObjectMethod(packageManager, getPackageInfoMethodId,
packageName, signaturesFlag);
printLog("packageInfo obtained.");
jclass packageInfoClazz = env->FindClass("android/content/pm/PackageInfo");
jfieldID signatureFieldId = env->GetFieldID(packageInfoClazz, "signatures",
"[Landroid/content/pm/Signature;");
jobjectArray signatures = (jobjectArray) env->GetObjectField(packageInfo, signatureFieldId);
printLog("signatures obtained.");
jclass signatureClazz = env->FindClass("android/content/pm/Signature");
jmethodID toByteArrayMethodId = env->GetMethodID(signatureClazz, "toByteArray", "()[B");
jbyteArray cert = (jbyteArray) env->CallObjectMethod(env->GetObjectArrayElement(signatures, 0),
toByteArrayMethodId);
printLog("cert obtained.");
jclass inputStreamClazz = env->FindClass("java/io/ByteArrayInputStream");
jmethodID inputStreamConstructorMethodId = env->GetMethodID(inputStreamClazz, "",
"([B)V");
jobject inputStream = env->NewObject(inputStreamClazz, inputStreamConstructorMethodId, cert);
printLog("inputStream obtained.");
jclass certificateFactoryClazz = env->FindClass("java/security/cert/CertificateFactory");
jmethodID certificateFactoryGetInstanceMethodId = env->GetStaticMethodID(
certificateFactoryClazz,
"getInstance",
"(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
jstring x509String = env->NewStringUTF("X509");
jobject certificateFactory = env->CallStaticObjectMethod(certificateFactoryClazz,
certificateFactoryGetInstanceMethodId,
x509String);
printLog("certificateFactory obtained.");
jmethodID generateCertificateMethodId = env->GetMethodID(certificateFactoryClazz,
"generateCertificate",
"(Ljava/io/InputStream;)Ljava/security/cert/Certificate;");
jclass x509CertificateClazz = env->FindClass("java/security/cert/X509Certificate");
jobject x509Certificate = env->CallObjectMethod(certificateFactory, generateCertificateMethodId,
inputStream);
printLog("x509Certificate obtained.");
jclass messageDigestClazz = env->FindClass("java/security/MessageDigest");
jmethodID messageDigestInstanceMethodId = env->GetStaticMethodID(messageDigestClazz,
"getInstance",
"(Ljava/lang/String;)Ljava/security/MessageDigest;");
jstring sha1 = env->NewStringUTF("SHA1");
jobject messageDigest = env->CallStaticObjectMethod(messageDigestClazz,
messageDigestInstanceMethodId, sha1);
printLog("messageDigest obtained.");
jmethodID certificateEncodeMethodId = env->GetMethodID(x509CertificateClazz, "getEncoded",
"()[B");
jbyteArray certEncode = (jbyteArray) env->CallObjectMethod(x509Certificate,
certificateEncodeMethodId);
printLog("certEncode obtained.");
jmethodID messageDigestMethodId = env->GetMethodID(messageDigestClazz, "digest", "([B)[B");
jbyteArray digestArray = (jbyteArray) env->CallObjectMethod(messageDigest,
messageDigestMethodId,
certEncode);
printLog("digestArray obtained.");
jclass base64Clazz = env->FindClass("android/util/Base64");
jmethodID encodeToStringMethodId = env->GetStaticMethodID(base64Clazz, "encodeToString",
"([BI)Ljava/lang/String;");
jfieldID base64FlagFiledId = env->GetStaticFieldID(base64Clazz, "DEFAULT",
"I");
jint base64DefaultFlag = env->GetStaticIntField(base64Clazz, base64FlagFiledId);
jstring result = (jstring) env->CallStaticObjectMethod(base64Clazz, encodeToStringMethodId,
digestArray, base64DefaultFlag);
printLog("result obtained.");
env->DeleteLocalRef(packageManager);
env->DeleteLocalRef(packageNames);
env->DeleteLocalRef(packageInfo);
env->DeleteLocalRef(signatures);
env->DeleteLocalRef(cert);
env->DeleteLocalRef(inputStream);
env->DeleteLocalRef(certificateFactory);
env->DeleteLocalRef(x509Certificate);
env->DeleteLocalRef(messageDigest);
env->DeleteLocalRef(certEncode);
env->DeleteLocalRef(digestArray);
return result;
}
int check_facet_id(JNIEnv *env, jstring facet_id) {
// modify to your app's facet wallpaperId
jstring valid_facet_id_string = env->NewStringUTF("unkown");
const char *valid_facet_id = env->GetStringUTFChars(valid_facet_id_string, 0);
char *target_facet_id = (char *) env->GetStringUTFChars(facet_id, 0);
// target_facet_id[strcspn(target_facet_id, "\r\n")] = '\0';
printLog(target_facet_id);
printLog(valid_facet_id);
int result = strcmp(target_facet_id, valid_facet_id);
char tmp[128];
sprintf(tmp, "check complete: result=%d", result);
printLog(tmp);
env->DeleteLocalRef(valid_facet_id_string);
return result;
}
void printLog(const char *str) {
if (LOG_ENABLE == 0) {
return;
}
LOGI(str, LOG);
}
================================================
FILE: data/src/main/java/com/yalin/style/data/SyncConfig.java
================================================
package com.yalin.style.data;
import java.util.concurrent.TimeUnit;
/**
* YaLin 2017/1/3.
*/
public class SyncConfig {
public static final int DEFAULT_DOWNLOAD_TIMEOUT = 120; // in seconds
public static final int DEFAULT_READ_TIMEOUT = 30; // in seconds
public static final int DEFAULT_CONNECT_TIMEOUT = 15; // in seconds
public static final long DEBUG_AUTO_SYNC_INTERVAL_LONG =
TimeUnit.MILLISECONDS.convert(5L, TimeUnit.MINUTES);
public static final long AUTO_SYNC_INTERVAL_LONG =
TimeUnit.MILLISECONDS.convert(16L, TimeUnit.HOURS);
}
================================================
FILE: data/src/main/java/com/yalin/style/data/cache/AdvanceWallpaperCache.kt
================================================
package com.yalin.style.data.cache
import com.yalin.style.data.entity.AdvanceWallpaperEntity
/**
* @author jinyalin
* @since 2017/8/4.
*/
interface AdvanceWallpaperCache {
fun put(wallpapers: List)
fun getSelectedWallpaper(): AdvanceWallpaperEntity?
fun selectWallpaper(wallpaperId: String)
fun getWallpapers(): List
fun getWallpaper(wallpaperId: String): AdvanceWallpaperEntity?
fun readAd(wallpaperId: String)
fun evictAll()
fun isCached(wallpaperId: String): Boolean
fun isDirty(): Boolean
fun makeDirty()
}
================================================
FILE: data/src/main/java/com/yalin/style/data/cache/AdvanceWallpaperCacheImpl.kt
================================================
package com.yalin.style.data.cache
import android.text.TextUtils
import com.yalin.style.data.entity.AdvanceWallpaperEntity
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/8/4.
*/
@Singleton
class AdvanceWallpaperCacheImpl @Inject constructor() : AdvanceWallpaperCache {
private var wallpapers: List? = null
@Synchronized override fun put(wallpapers: List) {
this.wallpapers = wallpapers
}
override fun getSelectedWallpaper(): AdvanceWallpaperEntity? {
if (isDirty()) {
return null
}
return wallpapers!!.firstOrNull { it.isSelected }
}
override fun selectWallpaper(wallpaperId: String) {
if (isDirty()) {
throw IllegalStateException("Cache is dirty.")
}
for (wallpaper in wallpapers!!) {
wallpaper.isSelected = TextUtils.equals(wallpaperId, wallpaper.wallpaperId)
}
}
override fun getWallpapers(): List {
if (isDirty()) {
throw IllegalStateException("Cache is dirty.")
}
return ArrayList(wallpapers!!)
}
override fun getWallpaper(wallpaperId: String): AdvanceWallpaperEntity? {
if (!isCached(wallpaperId)) {
throw IllegalStateException("WallpaperId $wallpaperId is not cached.")
}
return wallpapers!!.firstOrNull { TextUtils.equals(wallpaperId, it.wallpaperId) }
}
override fun readAd(wallpaperId: String) {
if (isCached(wallpaperId)) {
getWallpaper(wallpaperId)?.needAd = false
}
}
@Synchronized override fun evictAll() {
wallpapers = null
}
override fun isCached(wallpaperId: String): Boolean {
if (isDirty()) {
return false
}
return wallpapers!!.any { TextUtils.equals(wallpaperId, it.wallpaperId) }
}
override fun isDirty(): Boolean {
return wallpapers == null || wallpapers!!.isEmpty()
}
override fun makeDirty() {
evictAll()
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/cache/GalleryWallpaperCache.kt
================================================
package com.yalin.style.data.cache
import com.yalin.style.data.entity.GalleryWallpaperEntity
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/26.
*/
@Singleton
class GalleryWallpaperCache @Inject constructor() {
private var wallpaperEntities: List? = null
fun put(wallpaperEntities: List) {
this.wallpaperEntities = wallpaperEntities
}
fun get() = wallpaperEntities
fun isCached() = wallpaperEntities != null && wallpaperEntities!!.isNotEmpty()
fun isDirty() = wallpaperEntities == null || wallpaperEntities!!.isEmpty()
fun evictAll() {
wallpaperEntities = null
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/cache/SourcesCache.kt
================================================
package com.yalin.style.data.cache
import android.content.Context
import com.yalin.style.data.entity.SourceEntity
import io.reactivex.Observable
/**
* @author jinyalin
* @since 2017/5/22.
*/
interface SourcesCache {
fun getSources(ctx: Context): Observable>
fun selectSource(selectSourceId: Int, tempSelect: Boolean): Boolean
fun getUsedSourceId(): Int
}
================================================
FILE: data/src/main/java/com/yalin/style/data/cache/SourcesCacheImpl.kt
================================================
package com.yalin.style.data.cache
import android.content.Context
import android.graphics.Color
import com.yalin.style.data.R
import com.yalin.style.data.entity.SourceEntity
import com.yalin.style.data.extensions.DelegateExt
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.repository.datasource.sync.gallery.GalleryScheduleService
import com.yalin.style.domain.repository.SourcesRepository.*
import io.reactivex.Observable
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/22.
*/
@Singleton
class SourcesCacheImpl @Inject
constructor(val ctx: Context) : SourcesCache {
var selectedId: Int by DelegateExt.preferences(ctx, "selected_source_id", SOURCE_ID_STYLE)
val advanceSource: SourceEntity
val featureSource: SourceEntity
val gallerySource: SourceEntity
init {
advanceSource = SourceEntity(SOURCE_ID_ADVANCE).apply {
title = ctx.getString(R.string.advance_source_title)
iconId = R.drawable.style_ic_source
description = ctx.getString(R.string.advance_source_description)
color = Color.WHITE
isSelected = selectedId == id
isHasSetting = true
}
featureSource = SourceEntity(SOURCE_ID_STYLE).apply {
title = ctx.getString(R.string.featuredart_source_title)
iconId = R.drawable.style_ic_source
description = ctx.getString(R.string.featuredart_source_description)
color = Color.WHITE
isSelected = selectedId == id
isHasSetting = false
}
gallerySource = SourceEntity(SOURCE_ID_CUSTOM).apply {
title = ctx.getString(R.string.gallery_title)
iconId = R.drawable.gallery_ic_source
description = ctx.getString(R.string.gallery_description)
color = 0x4db6ac
isSelected = selectedId == id
isHasSetting = true
}
if (getUsedSourceId() == SOURCE_ID_CUSTOM) {
GalleryScheduleService.startUp(ctx)
}
}
override fun getSources(ctx: Context): Observable> {
return Observable.create { emitter ->
val sources = ArrayList()
sources.add(advanceSource)
sources.add(featureSource)
sources.add(gallerySource)
emitter.onNext(sources)
emitter.onComplete()
}
}
override fun selectSource(selectSourceId: Int, tempSelect: Boolean): Boolean {
var success = false
if (featureSource.id == selectSourceId) {
featureSource.isSelected = true
gallerySource.isSelected = false
advanceSource.isSelected = false
success = true
GalleryScheduleService.shutDown(ctx)
} else if (gallerySource.id == selectSourceId) {
featureSource.isSelected = false
gallerySource.isSelected = true
advanceSource.isSelected = false
success = true
GalleryScheduleService.startUp(ctx)
} else if (advanceSource.id == selectSourceId) {
featureSource.isSelected = false
gallerySource.isSelected = false
advanceSource.isSelected = true
success = true
GalleryScheduleService.shutDown(ctx)
}
selectedId = selectSourceId
if (success) {
notifyChanged()
}
return success
}
override fun getUsedSourceId(): Int {
return selectedId
}
private fun notifyChanged() {
ctx.contentResolver.notifyChange(StyleContract.Source.CONTENT_URI, null)
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/cache/WallpaperCache.java
================================================
package com.yalin.style.data.cache;
import com.yalin.style.data.entity.WallpaperEntity;
import java.util.Queue;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/20.
*/
public interface WallpaperCache {
/**
* return the first
*
* @return wallpaperEntity
*/
Observable get();
Observable getNext();
Observable getWallpaperCount();
void likeWallpaper(String wallpaperId);
void put(Queue wallpaperEntities);
boolean isCached();
boolean isCached(String wallpaperId);
boolean isDirty();
void evictAll();
}
================================================
FILE: data/src/main/java/com/yalin/style/data/cache/WallpaperCacheImpl.java
================================================
package com.yalin.style.data.cache;
import android.text.TextUtils;
import com.fernandocejas.arrow.checks.Preconditions;
import com.yalin.style.data.entity.WallpaperEntity;
import java.util.Queue;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/20.
*/
@Singleton
public class WallpaperCacheImpl implements WallpaperCache {
private Queue wallpaperEntities;
@Inject
public WallpaperCacheImpl() {
}
@Override
public Observable get() {
Preconditions.checkNotNull(wallpaperEntities, "There is not cached wallpaper.");
Preconditions.checkArgument(!wallpaperEntities.isEmpty(), "There is not cached wallpaper.");
return Observable.create(emitter -> {
emitter.onNext(wallpaperEntities.peek());
emitter.onComplete();
});
}
@Override
public Observable getNext() {
Preconditions.checkNotNull(wallpaperEntities, "There is not cached wallpaper.");
Preconditions.checkArgument(!wallpaperEntities.isEmpty(), "There is not cached wallpaper.");
return Observable.create(emitter -> {
WallpaperEntity entity = wallpaperEntities.poll();
wallpaperEntities.offer(entity);
emitter.onNext(wallpaperEntities.peek());
emitter.onComplete();
});
}
@Override
public Observable getWallpaperCount() {
Preconditions.checkNotNull(wallpaperEntities, "There is not cached wallpaper.");
return Observable.create(emitter -> {
emitter.onNext(wallpaperEntities.size());
emitter.onComplete();
});
}
@Override
public void likeWallpaper(String wallpaperId) {
Preconditions.checkNotNull(wallpaperId, "There is not cached wallpaper.");
if (isCached(wallpaperId)) {
WallpaperEntity entity = get(wallpaperId);
if (entity != null) {
entity.liked = !entity.liked;
}
}
}
@Override
public synchronized void put(Queue wallpaperEntities) {
this.wallpaperEntities = wallpaperEntities;
}
@Override
public boolean isCached() {
return wallpaperEntities != null && !wallpaperEntities.isEmpty();
}
@Override
public boolean isCached(String wallpaperId) {
if (isDirty()) {
return false;
}
for (WallpaperEntity entity : wallpaperEntities) {
if (TextUtils.equals(entity.wallpaperId, wallpaperId)) {
return true;
}
}
return false;
}
@Override
public boolean isDirty() {
return wallpaperEntities == null;
}
@Override
public synchronized void evictAll() {
if (wallpaperEntities != null) {
wallpaperEntities.clear();
}
wallpaperEntities = null;
}
private WallpaperEntity get(String wallpaperId) {
for (WallpaperEntity entity : wallpaperEntities) {
if (TextUtils.equals(entity.wallpaperId, wallpaperId)) {
return entity;
}
}
return null;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/AdvanceWallpaperEntity.java
================================================
package com.yalin.style.data.entity;
import android.database.Cursor;
import android.text.TextUtils;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* @author jinyalin
* @since 2017/7/28.
*/
public class AdvanceWallpaperEntity {
private static final String TAG = "AdvanceWallpaperEntity";
public int id;
public String wallpaperId;
public String link;
public String name;
public String author;
public String iconUrl;
public String downloadUrl;
public String providerName;
public boolean lazyDownload;
public boolean needAd;
public String storePath;
public String checkSum;
public boolean isDefault = false;
public boolean isSelected = false;
public static List readCursor(Cursor cursor) {
List validWallpapers = new ArrayList<>();
while (cursor != null && cursor.moveToNext()) {
AdvanceWallpaperEntity wallpaperEntity = readEntityFromCursor(cursor);
try {
if (!wallpaperEntity.lazyDownload
&& !new File(wallpaperEntity.storePath).exists()) {
throw new FileNotFoundException("Component not found.");
}
validWallpapers.add(wallpaperEntity);
} catch (Exception e) {
LogUtil.D(TAG, "File not found with wallpaper wallpaperId : "
+ wallpaperEntity.wallpaperId);
}
}
return validWallpapers;
}
public static AdvanceWallpaperEntity readEntityFromCursor(Cursor cursor) {
AdvanceWallpaperEntity wallpaperEntity = new AdvanceWallpaperEntity();
wallpaperEntity.id = cursor.getInt(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper._ID));
wallpaperEntity.name = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_NAME));
wallpaperEntity.wallpaperId = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_WALLPAPER_ID));
wallpaperEntity.iconUrl = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_ICON_URL));
wallpaperEntity.link = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_LINK));
wallpaperEntity.author = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_AUTHOR));
wallpaperEntity.downloadUrl = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_DOWNLOAD_URL));
wallpaperEntity.checkSum = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_CHECKSUM));
wallpaperEntity.storePath = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_STORE_PATH));
wallpaperEntity.providerName = cursor.getString(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_PROVIDER_NAME));
wallpaperEntity.isSelected = cursor.getInt(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_SELECTED)) == 1;
wallpaperEntity.lazyDownload = cursor.getInt(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_LAZY_DOWNLOAD)) == 1;
wallpaperEntity.needAd = cursor.getInt(cursor.getColumnIndex(
StyleContract.AdvanceWallpaper.COLUMN_NAME_NEED_AD)) == 1;
return wallpaperEntity;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AdvanceWallpaperEntity) {
if (TextUtils.equals(((AdvanceWallpaperEntity) obj).name, name)
&& TextUtils.equals(((AdvanceWallpaperEntity) obj).checkSum, checkSum)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
int result = 0;
result = 31 * result + name.hashCode();
result = 31 * result + checkSum.hashCode();
return result;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/DeviceInfo.java
================================================
package com.yalin.style.data.entity;
import com.yalin.style.data.BuildConfig;
/**
* @author jinyalin
* @since 2017/4/25.
*/
public class DeviceInfo {
private int sdkVersion;
private String androidId;
private String manufacturer;
private String brand;
private String model;
private String type;
private String versionName;
private int versionCode;
private String channel;
public DeviceInfo(int sdkVersion, String androidId, String manufacturer,
String brand, String model) {
this.sdkVersion = sdkVersion;
this.androidId = androidId;
this.manufacturer = manufacturer;
this.brand = brand;
this.model = model;
type = "Android";
versionName = BuildConfig.VERSION_NAME;
versionCode = BuildConfig.VERSION_CODE;
channel = BuildConfig.CHANNEL;
}
public int getSdkVersion() {
return sdkVersion;
}
public String getAndroidId() {
return androidId;
}
public String getManufacturer() {
return manufacturer;
}
public String getModel() {
return model;
}
public String getType() {
return type;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/GalleryWallpaperEntity.kt
================================================
package com.yalin.style.data.entity
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import com.yalin.style.data.log.LogUtil
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.utils.getCacheFileForUri
import com.yalin.style.data.utils.getImagesFromTreeUri
import java.io.InputStream
import java.util.ArrayList
/**
* @author jinyalin
* @since 2017/5/24.
*/
class GalleryWallpaperEntity {
var id: Long = 0
var uri: String? = null
var isTreeUri: Boolean = false
var dateTime: Long = 0
var location: String? = null
var hasMetadata: Boolean = false
companion object {
private val TAG = "GalleryWallpaperEntity"
fun readCursor(context: Context, cursor: Cursor?): ArrayList {
val validWallpapers = ArrayList(5)
while (cursor != null && cursor.moveToNext()) {
val entity = readEntityFromCursor(cursor)
var inputStream: InputStream? = null
try {
// valid input stream
if (!entity.isTreeUri) {
inputStream = context.contentResolver.openInputStream(
Uri.parse(entity.uri))
}
validWallpapers.add(entity)
} catch (e: Exception) {
LogUtil.D(TAG, "Cannot open inputStream for uri : "
+ entity.uri)
val cacheFile = getCacheFileForUri(context, entity.uri!!)
if ((cacheFile != null && cacheFile.exists())) {
// has cache file
validWallpapers.add(entity)
}
} finally {
inputStream?.close()
}
}
return validWallpapers;
}
fun readEntityFromCursor(cursor: Cursor): GalleryWallpaperEntity {
val entity = GalleryWallpaperEntity()
entity.id = cursor.getLong(cursor.getColumnIndex(
StyleContract.GalleryWallpaper._ID))
entity.uri = cursor.getString(cursor.getColumnIndex(
StyleContract.GalleryWallpaper.COLUMN_NAME_CUSTOM_URI))
entity.isTreeUri = cursor.getInt(cursor.getColumnIndex(
StyleContract.GalleryWallpaper.COLUMN_NAME_IS_TREE_URI)) == 1
entity.dateTime = cursor.getLong(cursor.getColumnIndex(
StyleContract.GalleryWallpaper.COLUMN_NAME_DATE_TIME))
entity.location = cursor.getString(cursor.getColumnIndex(
StyleContract.GalleryWallpaper.COLUMN_NAME_LOCATION))
entity.hasMetadata = cursor.getInt(cursor.getColumnIndex(
StyleContract.GalleryWallpaper.COLUMN_NAME_HAS_METADATA)) == 1
return entity
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/HttpRequestBody.java
================================================
package com.yalin.style.data.entity;
import android.content.Context;
import com.yalin.style.data.utils.FacetIdUtil;
/**
* @author jinyalin
* @since 2017/4/25.
*/
public class HttpRequestBody {
private DeviceInfo deviceInfo;
private String facetId;
public HttpRequestBody(Context context, DeviceInfo deviceInfo) {
this.deviceInfo = deviceInfo;
this.facetId = FacetIdUtil.getFacetId(context);
}
public DeviceInfo getDeviceInfo() {
return deviceInfo;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/SourceEntity.kt
================================================
package com.yalin.style.data.entity
/**
* @author jinyalin
* *
* @since 2017/5/23.
*/
class SourceEntity(var id: Int) {
var title: String? = null
var description: String? = null
var iconId: Int = 0
var isSelected: Boolean = false
var isHasSetting: Boolean = false
var color: Int = 0
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/WallpaperEntity.java
================================================
package com.yalin.style.data.entity;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract;
import com.yalin.style.data.repository.datasource.provider.StyleContract.Wallpaper;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public class WallpaperEntity {
private static final String TAG = "WallpaperEntity";
public int id;
public String wallpaperId;
public String imageUri;
public String title;
public String byline;
public String attribution;
public boolean canLike;
public boolean liked;
public boolean isDefault;
public String checksum;
public WallpaperEntity() {
}
public static List readCursor(Context context, Cursor cursor) {
List validWallpapers = new ArrayList<>(5);
while (cursor != null && cursor.moveToNext()) {
WallpaperEntity wallpaperEntity = WallpaperEntity.readEntityFromCursor(cursor);
try {
// valid input stream
InputStream is = context.getContentResolver().openInputStream(
StyleContract.Wallpaper.buildWallpaperUri(
wallpaperEntity.wallpaperId));
validWallpapers.add(wallpaperEntity);
if (is != null) {
is.close();
}
} catch (Exception e) {
LogUtil.D(TAG, "File not found with wallpaper wallpaperId : "
+ wallpaperEntity.wallpaperId);
}
}
return validWallpapers;
}
public static WallpaperEntity readEntityFromCursor(Cursor cursor) {
WallpaperEntity wallpaperEntity = new WallpaperEntity();
wallpaperEntity.id = cursor.getInt(cursor.getColumnIndex(
Wallpaper._ID));
wallpaperEntity.title = cursor.getString(cursor.getColumnIndex(
Wallpaper.COLUMN_NAME_TITLE));
wallpaperEntity.wallpaperId = cursor.getString(cursor.getColumnIndex(
Wallpaper.COLUMN_NAME_WALLPAPER_ID));
wallpaperEntity.imageUri = cursor.getString(cursor.getColumnIndex(
Wallpaper.COLUMN_NAME_IMAGE_URI));
wallpaperEntity.byline = cursor.getString(cursor.getColumnIndex(
Wallpaper.COLUMN_NAME_BYLINE));
wallpaperEntity.attribution = cursor.getString(cursor.getColumnIndex(
Wallpaper.COLUMN_NAME_ATTRIBUTION));
wallpaperEntity.liked = cursor.getInt(cursor.getColumnIndex(
Wallpaper.COLUMN_NAME_LIKED)) == 1;
wallpaperEntity.checksum = cursor.getString(cursor.getColumnIndex(
Wallpaper.COLUMN_NAME_CHECKSUM));
return wallpaperEntity;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof WallpaperEntity) {
if (TextUtils.equals(((WallpaperEntity) obj).title, title)
&& TextUtils.equals(((WallpaperEntity) obj).checksum, checksum)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
int result = 0;
result = 31 * result + title.hashCode();
result = 31 * result + checksum.hashCode();
return result;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/mapper/AdvanceWallpaperEntityMapper.java
================================================
package com.yalin.style.data.entity.mapper;
import com.fernandocejas.arrow.checks.Preconditions;
import com.yalin.style.data.entity.AdvanceWallpaperEntity;
import com.yalin.style.domain.AdvanceWallpaper;
import com.yalin.style.domain.Wallpaper;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* @author jinyalin
* @since 2017/7/28.
*/
@Singleton
public class AdvanceWallpaperEntityMapper {
@Inject
public AdvanceWallpaperEntityMapper() {
}
public AdvanceWallpaper transform(AdvanceWallpaperEntity wallpaperEntity) {
Preconditions.checkNotNull(wallpaperEntity, "Wallpaper can not be null.");
AdvanceWallpaper wallpaper = new AdvanceWallpaper();
wallpaper.id = wallpaperEntity.id;
wallpaper.wallpaperId = wallpaperEntity.wallpaperId;
wallpaper.link = wallpaperEntity.link;
wallpaper.name = wallpaperEntity.name;
wallpaper.author = wallpaperEntity.author;
wallpaper.iconUrl = wallpaperEntity.iconUrl;
wallpaper.downloadUrl = wallpaperEntity.downloadUrl;
wallpaper.providerName = wallpaperEntity.providerName;
wallpaper.storePath = wallpaperEntity.storePath;
wallpaper.isDefault = wallpaperEntity.isDefault;
wallpaper.isSelected = wallpaperEntity.isSelected;
wallpaper.lazyDownload = wallpaperEntity.lazyDownload;
wallpaper.needAd = wallpaperEntity.needAd;
return wallpaper;
}
public List transformList(List wallpaperEntities) {
Preconditions.checkNotNull(wallpaperEntities, "SourceEntity can not be null.");
List sources = new ArrayList<>();
for (AdvanceWallpaperEntity entity : wallpaperEntities) {
sources.add(transform(entity));
}
return sources;
}
public Wallpaper mapToWallpaper(AdvanceWallpaperEntity wallpaperEntity) {
Preconditions.checkNotNull(wallpaperEntity, "Wallpaper can not be null.");
Wallpaper wallpaper = new Wallpaper();
wallpaper.wallpaperId = wallpaperEntity.wallpaperId;
wallpaper.byline = wallpaperEntity.author;
wallpaper.title = wallpaperEntity.name;
wallpaper.attribution = wallpaperEntity.link;
wallpaper.canLike = false;
return wallpaper;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/entity/mapper/WallpaperEntityMapper.java
================================================
package com.yalin.style.data.entity.mapper;
import com.fernandocejas.arrow.checks.Preconditions;
import com.yalin.style.data.entity.GalleryWallpaperEntity;
import com.yalin.style.data.entity.SourceEntity;
import com.yalin.style.data.entity.WallpaperEntity;
import com.yalin.style.domain.GalleryWallpaper;
import com.yalin.style.domain.Source;
import com.yalin.style.domain.Wallpaper;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* @author jinyalin
* @since 2017/4/18.
*/
@Singleton
public class WallpaperEntityMapper {
@Inject
public WallpaperEntityMapper() {
}
public Wallpaper transform(WallpaperEntity wallpaperEntity) {
Preconditions.checkNotNull(wallpaperEntity, "Wallpaper can not be null.");
Wallpaper wallpaper = new Wallpaper();
wallpaper.title = wallpaperEntity.title;
wallpaper.attribution = wallpaperEntity.attribution;
wallpaper.byline = wallpaperEntity.byline;
wallpaper.imageUri = wallpaperEntity.imageUri;
wallpaper.wallpaperId = wallpaperEntity.wallpaperId;
wallpaper.liked = wallpaperEntity.liked;
wallpaper.isDefault = wallpaperEntity.isDefault;
wallpaper.canLike = wallpaperEntity.canLike;
return wallpaper;
}
public List transformSources(List sourceEntities) {
Preconditions.checkNotNull(sourceEntities, "SourceEntity can not be null.");
List sources = new ArrayList<>();
for (SourceEntity entity : sourceEntities) {
sources.add(transformSource(entity));
}
return sources;
}
public Source transformSource(SourceEntity sourceEntity) {
Preconditions.checkNotNull(sourceEntity, "SourceEntity can not be null.");
Source source = new Source();
source.id = sourceEntity.getId();
source.title = sourceEntity.getTitle();
source.description = sourceEntity.getDescription();
source.iconId = sourceEntity.getIconId();
source.selected = sourceEntity.isSelected();
source.hasSetting = sourceEntity.isHasSetting();
source.color = sourceEntity.getColor();
return source;
}
public List transformGalleryWallpaper(
List galleryWallpaperEntities) {
Preconditions.checkNotNull(galleryWallpaperEntities,
"GalleryWallpaperEntity can not be null.");
List entities = new ArrayList<>();
for (GalleryWallpaperEntity entity : galleryWallpaperEntities) {
entities.add(transformGalleryWallpaper(entity));
}
return entities;
}
public GalleryWallpaper transformGalleryWallpaper(
GalleryWallpaperEntity galleryWallpaperEntity) {
Preconditions.checkNotNull(galleryWallpaperEntity,
"GalleryWallpaperEntity can not be null.");
GalleryWallpaper galleryWallpaper = new GalleryWallpaper();
galleryWallpaper.id = galleryWallpaperEntity.getId();
galleryWallpaper.isTreeUri = galleryWallpaperEntity.isTreeUri();
galleryWallpaper.uri = galleryWallpaperEntity.getUri();
galleryWallpaper.dateTime = galleryWallpaperEntity.getDateTime();
galleryWallpaper.location = galleryWallpaperEntity.getLocation();
galleryWallpaper.hasMetadata = galleryWallpaperEntity.getHasMetadata();
return galleryWallpaper;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/exception/LikeException.java
================================================
package com.yalin.style.data.exception;
/**
* @author jinyalin
* @since 2017/4/29.
*/
public class LikeException extends Exception {
public LikeException() {
super();
}
public LikeException(final Throwable cause) {
super(cause);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/exception/NetworkConnectionException.java
================================================
package com.yalin.style.data.exception;
/**
* @author jinyalin
* @since 2017/4/29.
*/
public class NetworkConnectionException extends Exception {
public NetworkConnectionException() {
super();
}
public NetworkConnectionException(final Throwable cause) {
super(cause);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/exception/NoContentException.java
================================================
package com.yalin.style.data.exception;
/**
* @author jinyalin
* @since 2017/7/31.
*/
public class NoContentException extends Exception {
}
================================================
FILE: data/src/main/java/com/yalin/style/data/exception/RemoteServerException.java
================================================
package com.yalin.style.data.exception;
/**
* @author jinyalin
* @since 2017/8/4.
*/
public class RemoteServerException extends Exception {
}
================================================
FILE: data/src/main/java/com/yalin/style/data/exception/ReswitchException.java
================================================
package com.yalin.style.data.exception;
/**
* @author jinyalin
* @since 2017/4/29.
*/
public class ReswitchException extends Exception {
public ReswitchException() {
super();
}
public ReswitchException(final Throwable cause) {
super(cause);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/executor/JobExecutor.java
================================================
package com.yalin.style.data.executor;
import android.support.annotation.NonNull;
import com.yalin.style.domain.executor.ThreadExecutor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* @author jinyalin
* @since 2017/4/18.
*/
@Singleton
public class JobExecutor implements ThreadExecutor {
protected final ThreadPoolExecutor threadPoolExecutor;
@Inject
JobExecutor() {
threadPoolExecutor = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(), new JobThreadFactory());
}
@Override
public void execute(@NonNull Runnable command) {
threadPoolExecutor.execute(command);
}
private static class JobThreadFactory implements ThreadFactory {
private int counter = 0;
@Override
public Thread newThread(@NonNull Runnable runnable) {
return new Thread(runnable, "android_" + counter);
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/executor/SerialJobExecutor.java
================================================
package com.yalin.style.data.executor;
import android.support.annotation.NonNull;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import java.util.ArrayDeque;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* @author jinyalin
* @since 2017/4/18.
*/
@Singleton
public class SerialJobExecutor extends JobExecutor implements SerialThreadExecutor {
final ArrayDeque mTasks = new ArrayDeque<>();
Runnable mActive;
@Inject
SerialJobExecutor() {
super();
}
@Override
public synchronized void execute(@NonNull Runnable command) {
mTasks.offer(() -> {
try {
command.run();
} finally {
scheduleNext();
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
threadPoolExecutor.execute(mActive);
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/extensions/DelegateExt.kt
================================================
package com.yalin.style.data.extensions
import android.content.Context
import android.content.SharedPreferences
import java.lang.IllegalArgumentException
import kotlin.reflect.KProperty
/**
* @author jinyalin
* @since 2017/5/22.
*/
object DelegateExt {
fun preferences(context: Context, name: String, default: T) =
Preferences(context, name, default)
}
class Preferences(val context: Context, val name: String, val default: T) {
companion object {
val PREFERENCE_NAME = "style_preference"
}
val prefs: SharedPreferences by lazy {
context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreference(name, default)
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) =
putPreference(name, value)
@Suppress("UNCHECKED_CAST")
private fun findPreference(name: String, default: T): T = with(prefs) {
val res: Any = when (default) {
is Long -> getLong(name, default)
is String -> getString(name, default)
is Int -> getInt(name, default)
is Boolean -> getBoolean(name, default)
is Float -> getFloat(name, default)
else -> throw IllegalArgumentException("This type can not be saved into Preferences")
}
res as T
}
private fun putPreference(name: String, value: T) = with(prefs.edit()) {
when (value) {
is Long -> putLong(name, value)
is String -> putString(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
is Float -> putFloat(name, value)
else -> throw IllegalArgumentException("This type can't be saved into Preferences")
}.apply()
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/lock/LikeWallpaperLock.java
================================================
package com.yalin.style.data.lock;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.interactor.DefaultObserver;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
/**
* YaLin
* On 2017/4/30.
*/
@Singleton
public class LikeWallpaperLock extends ResourceLock {
@Inject
public LikeWallpaperLock(ThreadExecutor threadExecutor) {
super(threadExecutor);
}
@Override
protected Observable appendDelay(Observable observable) {
return observable.delaySubscription(500, TimeUnit.MILLISECONDS);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/lock/OpenInputStreamLock.java
================================================
package com.yalin.style.data.lock;
import com.yalin.style.domain.executor.ThreadExecutor;
import io.reactivex.Observable;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* YaLin
* On 2017/4/30.
*/
@Singleton
public class OpenInputStreamLock extends ResourceLock {
@Inject
public OpenInputStreamLock(ThreadExecutor threadExecutor) {
super(threadExecutor);
}
@Override
protected Observable appendDelay(Observable observable) {
return observable.delaySubscription(1, TimeUnit.SECONDS);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/lock/ResourceLock.java
================================================
package com.yalin.style.data.lock;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.interactor.DefaultObserver;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
/**
* YaLin
* On 2017/4/30.
*/
public abstract class ResourceLock {
private AtomicBoolean lock = new AtomicBoolean(false);
private final ThreadExecutor threadExecutor;
private DefaultObserver releaseObserver;
public ResourceLock(ThreadExecutor threadExecutor) {
this.threadExecutor = threadExecutor;
}
public synchronized boolean obtain() {
if (lock.get()) {
return false;
} else {
lock.set(true);
return true;
}
}
public synchronized void release() {
if (releaseObserver != null) {
releaseObserver.dispose();
}
releaseObserver = new DefaultObserver<>();
Observable observable = Observable.create(e -> {
lock.set(false);
e.onComplete();
});
observable = appendDelay(observable);
observable.subscribeOn(Schedulers.from(threadExecutor))
.subscribeWith(releaseObserver);
}
protected Observable appendDelay(Observable observable) {
return observable;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/lock/SelectSourceLock.java
================================================
package com.yalin.style.data.lock;
import com.yalin.style.domain.executor.ThreadExecutor;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.reactivex.Observable;
/**
* YaLin
* On 2017/4/30.
*/
@Singleton
public class SelectSourceLock extends ResourceLock {
@Inject
public SelectSourceLock(ThreadExecutor threadExecutor) {
super(threadExecutor);
}
@Override
protected Observable appendDelay(Observable observable) {
return observable.delaySubscription(1, TimeUnit.SECONDS);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/log/LogUtil.java
================================================
package com.yalin.style.data.log;
import android.os.Environment;
import android.os.Process;
import android.util.Log;
import com.yalin.style.data.BuildConfig;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.Calendar;
import java.util.Locale;
/**
* YaLin 2016/11/23.
*/
public class LogUtil {
private static final String FILE_NAME = "Style/stat_log.txt";
public static final boolean LOG_ENABLE = BuildConfig.DEMO_MODE || BuildConfig.LOG_ENABLE;
public static synchronized boolean isExternalLogEnabled() {
//noinspection SimplifiableIfStatement
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return false;
}
return LOG_ENABLE && BuildConfig.ENABLE_EXTERNAL_LOG;
}
public static void F(String tag, String msg, Throwable throwable) {
if (!LOG_ENABLE) {
return;
}
String stackTraces = Log.getStackTraceString(throwable);
if (msg == null) {
msg = "";
}
F(tag, msg + " :\n " + stackTraces);
}
public static void F(String tag, String msg) {
if (!LOG_ENABLE) {
return;
}
String procInfo = getProcessInfo();
Log.e(tag, procInfo + msg);
if (!isExternalLogEnabled()) {
return;
}
tag = tag + ":debug";
writeLog(tag, procInfo + msg);
}
public static void E(String tag, String msg, Throwable throwable) {
if (!LOG_ENABLE) {
return;
}
String stackTraces = Log.getStackTraceString(throwable);
if (msg == null) {
msg = "";
}
E(tag, msg + " :\n " + stackTraces);
}
public static void E(String tag, String msg) {
if (!LOG_ENABLE) {
return;
}
String procInfo = getProcessInfo();
Log.e(tag, procInfo + msg);
}
public static void D(String tag, String msg) {
if (!LOG_ENABLE) {
return;
}
String procInfo = getProcessInfo();
Log.d(tag, procInfo + msg);
}
private static String getProcessInfo() {
return "Process wallpaperId: " + Process.myPid()
+ " Thread wallpaperId: " + Thread.currentThread().getId() + " ";
}
private static synchronized void writeLog(String tag, String msg) {
internalWriteLog(FILE_NAME, tag, msg);
}
private static synchronized void internalWriteLog(String filename, String tag, String msg) {
try {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return;
}
File file = new File(Environment.getExternalStorageDirectory(), filename);
//noinspection ResultOfMethodCallIgnored
file.getParentFile().mkdirs();
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(file, true)));
String time = getCurrentTime();
bw.write(time + " " + tag + " \t" + msg + "\r\n");
bw.close();
} catch (Exception e) {
// ignore
}
}
private static String getCurrentTime() {
Calendar c = Calendar.getInstance();
return String.format(Locale.getDefault(), "%d-%02d-%02d %02d:%02d:%02d.%03d",
c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH),
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND),
c.get(Calendar.MILLISECOND));
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/observable/SourcesObservableImpl.kt
================================================
package com.yalin.style.data.observable
import android.content.Context
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
import com.yalin.style.data.cache.SourcesCache
import com.yalin.style.data.log.LogUtil
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.domain.interactor.DefaultObserver
import com.yalin.style.domain.observable.SourcesObservable
import java.util.HashSet
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/23.
*/
@Singleton
class SourcesObservableImpl @Inject
constructor(val context: Context,
val sourcesCache: SourcesCache) : SourcesObservable {
companion object {
val TAG = "WallpaperObservableImpl"
}
private val mObserverSet = HashSet>()
private val mContentObserver = object : ContentObserver(Handler()) {
override fun onChange(selfChange: Boolean, uri: Uri) {
LogUtil.D(TAG, "Source changed notify observer to reload.")
notifyObserver()
}
}
private var observerRegistered = false
override fun registerObserver(observer: DefaultObserver) {
synchronized(mObserverSet) {
mObserverSet.add(observer)
if (!observerRegistered) {
context.contentResolver
.registerContentObserver(StyleContract.Source.CONTENT_URI,
true, mContentObserver)
observerRegistered = true
}
}
}
override fun unregisterObserver(observer: DefaultObserver) {
synchronized(mObserverSet) {
mObserverSet.remove(observer)
if (mObserverSet.isEmpty()) {
context.contentResolver.unregisterContentObserver(mContentObserver)
observerRegistered = false
}
}
}
private fun notifyObserver() {
for (observer in mObserverSet) {
observer.onNext(null)
observer.onComplete()
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/observable/WallpaperObservableImpl.kt
================================================
package com.yalin.style.data.observable
import android.content.Context
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
import android.text.TextUtils
import com.yalin.style.data.cache.SourcesCache
import com.yalin.style.data.log.LogUtil
import com.yalin.style.data.repository.datasource.AdvanceWallpaperDataStoreFactory
import com.yalin.style.data.repository.datasource.StyleWallpaperDataStoreFactory
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.domain.interactor.DefaultObserver
import com.yalin.style.domain.observable.SourcesObservable
import com.yalin.style.domain.observable.WallpaperObservable
import com.yalin.style.domain.repository.SourcesRepository.SOURCE_ID_CUSTOM
import com.yalin.style.domain.repository.SourcesRepository.SOURCE_ID_STYLE
import com.yalin.style.domain.repository.SourcesRepository.SOURCE_ID_ADVANCE
import java.util.HashSet
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/23.
*/
@Singleton
class WallpaperObservableImpl @Inject
constructor(val context: Context,
val sourcesCache: SourcesCache,
val styleWallpaperDataStoreFactory: StyleWallpaperDataStoreFactory,
val advanceWallpaperDataStoreFactory: AdvanceWallpaperDataStoreFactory,
val sourcesObservable: SourcesObservable) :
WallpaperObservable {
companion object {
val TAG = "WallpaperObservableImpl"
}
private val mObserverSet = HashSet>()
private val mWallpaperObserver = object : ContentObserver(Handler()) {
override fun onChange(selfChange: Boolean, uri: Uri) {
LogUtil.D(TAG, "Wallpaper data changed notify observer to reload.")
if (sourcesCache.getUsedSourceId() == SOURCE_ID_CUSTOM
&& TextUtils.equals(uri.toString(),
StyleContract.GalleryWallpaper.CONTENT_URI.toString())) {
notifyObserver()
} else if (sourcesCache.getUsedSourceId() == SOURCE_ID_STYLE
&& TextUtils.equals(uri.toString(),
StyleContract.Wallpaper.CONTENT_URI.toString())) {
notifyObserver()
} else if (sourcesCache.getUsedSourceId() == SOURCE_ID_ADVANCE
&& TextUtils.equals(uri.toString(),
StyleContract.AdvanceWallpaper.CONTENT_URI.toString())) {
notifyObserver()
}
styleWallpaperDataStoreFactory.onDataRefresh()
advanceWallpaperDataStoreFactory.onDataRefresh()
}
}
private val sourceObserver = object : DefaultObserver() {
override fun onComplete() {
notifyObserver()
}
}
init {
context.contentResolver
.registerContentObserver(StyleContract.Wallpaper.CONTENT_URI,
true, mWallpaperObserver)
context.contentResolver
.registerContentObserver(StyleContract.GalleryWallpaper.CONTENT_URI,
true, mWallpaperObserver)
context.contentResolver
.registerContentObserver(StyleContract.AdvanceWallpaper.CONTENT_URI,
true, mWallpaperObserver)
sourcesObservable.registerObserver(sourceObserver)
}
override fun registerObserver(observer: DefaultObserver) {
synchronized(mObserverSet) {
mObserverSet.add(observer)
}
}
override fun unregisterObserver(observer: DefaultObserver) {
synchronized(mObserverSet) {
mObserverSet.remove(observer)
}
}
private fun notifyObserver() {
for (observer in mObserverSet) {
observer.onNext(null)
observer.onComplete()
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/AdvanceWallpaperDataRepository.kt
================================================
package com.yalin.style.data.repository
import android.content.Context
import com.yalin.style.data.entity.mapper.AdvanceWallpaperEntityMapper
import com.yalin.style.data.extensions.DelegateExt
import com.yalin.style.data.repository.datasource.AdvanceWallpaperDataStoreFactory
import com.yalin.style.domain.AdvanceWallpaper
import com.yalin.style.domain.GalleryWallpaper
import com.yalin.style.domain.Wallpaper
import com.yalin.style.domain.repository.WallpaperRepository
import io.reactivex.Observable
import java.io.InputStream
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/7/27.
*/
@Singleton
class AdvanceWallpaperDataRepository
@Inject constructor(val context: Context,
val factory: AdvanceWallpaperDataStoreFactory,
val wallpaperMapper: AdvanceWallpaperEntityMapper,
val styleRepository: StyleWallpaperDataRepository)
: WallpaperRepository {
var needRollback: Boolean by DelegateExt.preferences(context, "is_need_rollback",
false)
override fun getWallpaper(): Observable {
return Observable.create { emitter ->
emitter.onNext(wallpaperMapper.mapToWallpaper(factory.create().getWallpaperEntity()))
emitter.onComplete()
}
}
override fun switchWallpaper(): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
override fun openInputStream(wallpaperId: String?): Observable {
var id: String? = null
styleRepository.wallpaper.subscribe(
{ wallpaper -> id = wallpaper.wallpaperId })
return styleRepository.openInputStream(id)
}
override fun getWallpaperCount(): Observable {
return Observable.create { emitter ->
emitter.onNext(1)
emitter.onComplete()
}
}
override fun likeWallpaper(wallpaperId: String?): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
override fun addGalleryWallpaperUris(uris: MutableList?): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
override fun removeGalleryWallpaperUris(uris: MutableList?): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
override fun getGalleryWallpapers(): Observable> {
return Observable.create> { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
override fun getAdvanceWallpapers(): Observable> {
return factory.create().getAdvanceWallpapers().map(wallpaperMapper::transformList)
}
override fun loadAdvanceWallpapers(): Observable> {
return factory.createRemoteDataStore().getAdvanceWallpapers()
.map(wallpaperMapper::transformList)
}
override fun downloadAdvanceWallpaper(wallpaperId: String): Observable {
return factory.createRemoteDataStore().downloadWallpaper(wallpaperId)
}
override fun selectAdvanceWallpaper(wallpaperId: String, tempSelect: Boolean):
Observable {
return factory.create().selectWallpaper(wallpaperId, tempSelect)
}
override fun getAdvanceWallpaper(): AdvanceWallpaper {
return wallpaperMapper.transform(factory.create().getWallpaperEntity())
}
override fun readAdvanceAd(wallpaperId: String): Observable {
return factory.create().readAd(wallpaperId)
}
override fun foreNow(wallpaperUri: String?): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
override fun setGalleryUpdateInterval(intervalMin: Int): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
override fun getGalleryUpdateInterval(): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
}
}
fun markRollback() {
needRollback = true
}
fun maybeRollback() {
if (needRollback) {
factory.create().rollback()
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/GalleryWallpaperDataRepository.kt
================================================
package com.yalin.style.data.repository
import com.yalin.style.data.entity.mapper.WallpaperEntityMapper
import com.yalin.style.data.repository.datasource.GalleryWallpaperDataStoreFactory
import com.yalin.style.domain.AdvanceWallpaper
import com.yalin.style.domain.GalleryWallpaper
import com.yalin.style.domain.Wallpaper
import com.yalin.style.domain.repository.WallpaperRepository
import io.reactivex.Observable
import java.io.InputStream
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/24.
*/
@Singleton
class GalleryWallpaperDataRepository @Inject
constructor(val galleryWallpaperDataStoreFactory: GalleryWallpaperDataStoreFactory,
val wallpaperEntityMapper: WallpaperEntityMapper) :
WallpaperRepository {
override fun getWallpaper(): Observable =
galleryWallpaperDataStoreFactory.create()
.wallPaperEntity.map(wallpaperEntityMapper::transform)
override fun switchWallpaper(): Observable =
galleryWallpaperDataStoreFactory.create()
.switchWallPaperEntity().map(wallpaperEntityMapper::transform)
override fun openInputStream(wallpaperId: String?): Observable =
galleryWallpaperDataStoreFactory.create()
.openInputStream(wallpaperId)
override fun getWallpaperCount(): Observable =
galleryWallpaperDataStoreFactory.create()
.wallpaperCount
override fun likeWallpaper(wallpaperId: String): Observable =
galleryWallpaperDataStoreFactory.create()
.likeWallpaper(wallpaperId)
override fun addGalleryWallpaperUris(uris: List): Observable =
galleryWallpaperDataStoreFactory.create()
.addGalleryWallpaperUris(uris)
override fun removeGalleryWallpaperUris(uris: List): Observable =
galleryWallpaperDataStoreFactory.create()
.removeGalleryWallpaperUris(uris)
override fun getGalleryWallpapers(): Observable> =
galleryWallpaperDataStoreFactory.create()
.getGalleryWallpaperUris().map(wallpaperEntityMapper::transformGalleryWallpaper)
override fun getAdvanceWallpapers(): Observable> {
return Observable.create> { emitter ->
emitter.onError(IllegalStateException(
"GalleryWallpaperDataRepository have not advance wallpapers."))
}
}
override fun loadAdvanceWallpapers(): Observable> {
return Observable.create> { emitter ->
emitter.onError(IllegalStateException(
"GalleryWallpaperDataRepository cannot load advance wallpapers."))
}
}
override fun downloadAdvanceWallpaper(wallpaperId: String): Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"GalleryWallpaperDataRepository cannot download advance wallpapers."))
}
}
override fun selectAdvanceWallpaper(wallpaperId: String, tempSelect: Boolean):
Observable {
return Observable.create { emitter ->
emitter.onError(IllegalStateException(
"GalleryWallpaperDataRepository cannot select advance wallpapers."))
}
}
override fun getAdvanceWallpaper(): AdvanceWallpaper {
throw IllegalStateException(
"GalleryWallpaperDataRepository cannot get advance wallpapers.")
}
override fun readAdvanceAd(wallpaperId: String?): Observable {
throw IllegalStateException(
"GalleryWallpaperDataRepository cannot read advance ad.")
}
override fun foreNow(wallpaperUri: String): Observable =
galleryWallpaperDataStoreFactory.create().forceNow(wallpaperUri)
override fun setGalleryUpdateInterval(intervalMin: Int): Observable =
galleryWallpaperDataStoreFactory.create().setUpdateIntervalMin(intervalMin)
override fun getGalleryUpdateInterval(): Observable =
galleryWallpaperDataStoreFactory.create().getUpdateIntervalMin()
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/SourcesDataRepository.kt
================================================
package com.yalin.style.data.repository
import android.content.Context
import com.yalin.style.data.entity.mapper.WallpaperEntityMapper
import com.yalin.style.data.repository.datasource.SourcesDataStoreFactory
import com.yalin.style.domain.Source
import com.yalin.style.domain.repository.SourcesRepository
import com.yalin.style.domain.repository.SourcesRepository.SOURCE_ID_ADVANCE
import com.yalin.style.domain.repository.SourcesRepository.SOURCE_ID_CUSTOM
import com.yalin.style.domain.repository.WallpaperRepository
import io.reactivex.Observable
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/23.
*/
@Singleton
class SourcesDataRepository @Inject
constructor(val context: Context,
val sourcesDataStoreFactory: SourcesDataStoreFactory,
val wallpaperEntityMapper: WallpaperEntityMapper,
var styleWallpaperDataRepository: StyleWallpaperDataRepository,
var customWallpaperDataRepository: GalleryWallpaperDataRepository,
var advanceWallpaperDataRepository: AdvanceWallpaperDataRepository) : SourcesRepository {
override fun getSources(): Observable> {
val sourcesDataStore = sourcesDataStoreFactory.create()
return sourcesDataStore.getSources().map(wallpaperEntityMapper::transformSources)
}
override fun selectSource(sourceId: Int, tempSelect: Boolean): Observable {
val sourcesDataStore = sourcesDataStoreFactory.create()
return sourcesDataStore.selectSource(sourceId, tempSelect)
}
override fun getWallpaperRepository(): WallpaperRepository {
val sourcesDataStore = sourcesDataStoreFactory.create()
when (sourcesDataStore.getUsedSourceId()) {
SOURCE_ID_CUSTOM -> return customWallpaperDataRepository
SOURCE_ID_ADVANCE -> return advanceWallpaperDataRepository
else -> return styleWallpaperDataRepository
}
}
override fun getSelectedSource(): Int {
val sourcesDataStore = sourcesDataStoreFactory.create()
return sourcesDataStore.getUsedSourceId()
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/StyleWallpaperDataRepository.java
================================================
package com.yalin.style.data.repository;
import android.content.Context;
import android.text.TextUtils;
import com.fernandocejas.arrow.checks.Preconditions;
import com.yalin.style.data.entity.mapper.WallpaperEntityMapper;
import com.yalin.style.data.repository.datasource.WallpaperDataStore;
import com.yalin.style.data.repository.datasource.StyleWallpaperDataStoreFactory;
import com.yalin.style.data.repository.datasource.sync.SyncHelper;
import com.yalin.style.data.repository.datasource.sync.account.Account;
import com.yalin.style.domain.AdvanceWallpaper;
import com.yalin.style.domain.GalleryWallpaper;
import com.yalin.style.domain.Wallpaper;
import com.yalin.style.domain.repository.WallpaperRepository;
import java.io.InputStream;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/18.
*/
@Singleton
public class StyleWallpaperDataRepository implements WallpaperRepository {
private final Context context;
private final StyleWallpaperDataStoreFactory styleWallpaperDataStoreFactory;
private final WallpaperEntityMapper wallpaperEntityMapper;
@Inject
StyleWallpaperDataRepository(Context context,
StyleWallpaperDataStoreFactory styleWallpaperDataStoreFactory,
WallpaperEntityMapper wallpaperEntityMapper) {
this.context = context;
this.styleWallpaperDataStoreFactory = styleWallpaperDataStoreFactory;
this.wallpaperEntityMapper = wallpaperEntityMapper;
Account.createSyncAccount(context);
SyncHelper.updateSyncInterval(context);
}
@Override
public Observable getWallpaper() {
final WallpaperDataStore dataStore = styleWallpaperDataStoreFactory.create();
return dataStore.getWallPaperEntity().map(wallpaperEntityMapper::transform);
}
@Override
public Observable switchWallpaper() {
final WallpaperDataStore dataStore = styleWallpaperDataStoreFactory.create();
return dataStore.switchWallPaperEntity().map(wallpaperEntityMapper::transform);
}
@Override
public Observable openInputStream(String wallpaperId) {
Preconditions.checkArgument(!TextUtils.isEmpty(wallpaperId), "WallpaperId cannot be null");
final WallpaperDataStore dataStore = styleWallpaperDataStoreFactory.createDbDataStore();
return dataStore.openInputStream(wallpaperId);
}
@Override
public Observable getWallpaperCount() {
final WallpaperDataStore dataStore = styleWallpaperDataStoreFactory.create();
return dataStore.getWallpaperCount();
}
@Override
public Observable likeWallpaper(String wallpaperId) {
Preconditions.checkArgument(!TextUtils.isEmpty(wallpaperId), "WallpaperId cannot be null");
final WallpaperDataStore dataStore = styleWallpaperDataStoreFactory.createDbDataStore();
return dataStore.likeWallpaper(wallpaperId);
}
@Override
public Observable addGalleryWallpaperUris(List uris) {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository can not add gallery wallpaper."))
);
}
@Override
public Observable removeGalleryWallpaperUris(List uris) {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository can not remove gallery wallpaper."))
);
}
@Override
public Observable> getGalleryWallpapers() {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository have not gallery wallpapers."))
);
}
@Override
public Observable> getAdvanceWallpapers() {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository cannot get advance wallpapers."))
);
}
@Override
public Observable> loadAdvanceWallpapers() {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository cannot load advance wallpapers."))
);
}
@Override
public Observable downloadAdvanceWallpaper(String wallpaperId) {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository cannot download advance wallpapers."))
);
}
@Override
public Observable selectAdvanceWallpaper(String wallpaperId, boolean tempSelect) {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository cannot select advance wallpapers."))
);
}
public AdvanceWallpaper getAdvanceWallpaper() {
throw new IllegalStateException(
"StyleWallpaperRepository cannot get advance wallpapers.");
}
@Override
public Observable readAdvanceAd(String wallpaperId) {
throw new IllegalStateException(
"StyleWallpaperRepository cannot read advance ad.");
}
@Override
public Observable foreNow(String wallpaperUri) {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository can not foreNow gallery wallpaper."))
);
}
@Override
public Observable setGalleryUpdateInterval(int intervalMin) {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository can not setGalleryUpdateInterval gallery wallpaper."))
);
}
@Override
public Observable getGalleryUpdateInterval() {
return Observable.create(emitter ->
emitter.onError(new IllegalStateException(
"StyleWallpaperRepository can not getGalleryUpdateInterval gallery wallpaper."))
);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/AdvanceWallpaperDataStore.kt
================================================
package com.yalin.style.data.repository.datasource
import com.yalin.style.data.entity.AdvanceWallpaperEntity
import io.reactivex.Observable
/**
* @author jinyalin
* @since 2017/7/28.
*/
interface AdvanceWallpaperDataStore {
fun getWallpaperEntity(): AdvanceWallpaperEntity
fun getAdvanceWallpapers(): Observable>
fun selectWallpaper(wallpaperId: String, tempSelect: Boolean): Observable
fun downloadWallpaper(wallpaperId: String): Observable
fun readAd(wallpaperId: String): Observable
fun rollback()
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/AdvanceWallpaperDataStoreFactory.kt
================================================
package com.yalin.style.data.repository.datasource
import android.content.Context
import com.yalin.style.data.cache.AdvanceWallpaperCache
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/7/28.
*/
@Singleton
class AdvanceWallpaperDataStoreFactory @Inject
constructor(val context: Context,
val advanceWallpaperCache: AdvanceWallpaperCache) {
fun create(): AdvanceWallpaperDataStore {
return AdvanceWallpaperDataStoreImpl(context, advanceWallpaperCache)
}
fun createRemoteDataStore(): AdvanceWallpaperDataStore {
return RemoteAdvanceWallpaperDataStore(context,
AdvanceWallpaperDataStoreImpl(context, advanceWallpaperCache))
}
fun onDataRefresh() {
advanceWallpaperCache.evictAll()
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/AdvanceWallpaperDataStoreImpl.kt
================================================
package com.yalin.style.data.repository.datasource
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import com.yalin.style.data.cache.AdvanceWallpaperCache
import com.yalin.style.data.entity.AdvanceWallpaperEntity
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.utils.notifyChange
import io.reactivex.Observable
/**
* @author jinyalin
* @since 2017/7/28.
*/
class AdvanceWallpaperDataStoreImpl(val context: Context,
val advanceWallpaperCache: AdvanceWallpaperCache)
: AdvanceWallpaperDataStore {
companion object {
val TAG = "AdvanceDataStore"
val DEFAULT_WALLPAPER_ID = "-1"
}
@Synchronized override fun getWallpaperEntity(): AdvanceWallpaperEntity {
if (!advanceWallpaperCache.isDirty()) {
val selected = advanceWallpaperCache.getSelectedWallpaper()
if (selected != null) {
return selected
}
}
var cursor: Cursor? = null
var entity: AdvanceWallpaperEntity? = null
try {
val contentResolver = context.contentResolver
cursor = contentResolver.query(StyleContract.AdvanceWallpaper.CONTENT_SELECTED_URI,
null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
entity = AdvanceWallpaperEntity.readEntityFromCursor(cursor)
}
} finally {
if (cursor != null) {
cursor.close()
}
}
if (entity == null) {
entity = buildDefaultWallpaper()
}
return entity
}
override fun getAdvanceWallpapers(): Observable> {
return createAdvanceWallpapersFromDB().doOnNext(advanceWallpaperCache::put)
}
override fun selectWallpaper(wallpaperId: String, tempSelect: Boolean):
Observable {
return Observable.create { emitter ->
val selectValue = ContentValues()
selectValue.put(StyleContract.AdvanceWallpaper.COLUMN_NAME_SELECTED, 1)
val unselectedValue = ContentValues()
unselectedValue.put(StyleContract.AdvanceWallpaper.COLUMN_NAME_SELECTED, 0)
// unselected old
context.contentResolver.update(
StyleContract.AdvanceWallpaper.CONTENT_SELECTED_URI,
unselectedValue, null, null)
// select new
val uri = StyleContract.AdvanceWallpaper.buildWallpaperUri(wallpaperId)
val selectedCount = context.contentResolver.update(uri, selectValue, null, null)
if (selectedCount > 0) {
emitter.onNext(true)
} else {
emitter.onNext(false)
}
synchronized(advanceWallpaperCache) {
if (!advanceWallpaperCache.isDirty()) {
advanceWallpaperCache.selectWallpaper(wallpaperId)
}
}
emitter.onComplete()
notifyChange(context, StyleContract.AdvanceWallpaper.CONTENT_URI)
}
}
override fun downloadWallpaper(wallpaperId: String): Observable {
throw UnsupportedOperationException("Local data store not support download wallpaper.")
}
fun loadWallpaperEntity(wallpaperId: String): AdvanceWallpaperEntity {
var entity: AdvanceWallpaperEntity? = null
synchronized(advanceWallpaperCache) {
if (!advanceWallpaperCache.isDirty()
&& advanceWallpaperCache.isCached(wallpaperId)) {
entity = advanceWallpaperCache.getWallpaper(wallpaperId)
} else {
entity = loadWallpaperEntityFromDB(wallpaperId)
}
if (entity == null) {
entity = loadWallpaperEntityFromDB(wallpaperId)
}
}
return entity!!
}
override fun readAd(wallpaperId: String): Observable {
return Observable.create { emitter ->
if (advanceWallpaperCache.isCached(wallpaperId)) {
advanceWallpaperCache.readAd(wallpaperId)
}
val uri = StyleContract.AdvanceWallpaper.buildWallpaperUri(wallpaperId)
val contentValues = ContentValues()
contentValues.put(StyleContract.AdvanceWallpaper.COLUMN_NAME_NEED_AD, 0)
context.contentResolver.update(uri, contentValues, null, null)
emitter.onNext(true)
emitter.onComplete()
}
}
override fun rollback() {
advanceWallpaperCache.evictAll()
val unselectedValue = ContentValues()
unselectedValue.put(StyleContract.AdvanceWallpaper.COLUMN_NAME_SELECTED, 0)
// unselected all
context.contentResolver.update(
StyleContract.AdvanceWallpaper.CONTENT_SELECTED_URI,
unselectedValue, null, null)
}
private fun loadWallpaperEntityFromDB(wallpaperId: String): AdvanceWallpaperEntity? {
var cursor: Cursor? = null
try {
val contentResolver = context.contentResolver
cursor = contentResolver.query(StyleContract.AdvanceWallpaper
.buildWallpaperUri(wallpaperId),
null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val entity = AdvanceWallpaperEntity.readEntityFromCursor(cursor)
return entity
}
return null
} finally {
cursor?.close()
}
}
private fun createAdvanceWallpapersFromDB(): Observable> {
return Observable.create { emitter ->
var cursor: Cursor? = null
val validWallpapers = ArrayList()
try {
val contentResolver = context.contentResolver
cursor = contentResolver.query(StyleContract.AdvanceWallpaper.CONTENT_URI,
null, null, null, null)
validWallpapers.addAll(AdvanceWallpaperEntity.readCursor(cursor))
} finally {
cursor?.close()
}
emitter.onNext(validWallpapers)
emitter.onComplete()
}
}
private fun buildDefaultWallpaper(): AdvanceWallpaperEntity {
val entity = AdvanceWallpaperEntity()
entity.isDefault = true
entity.id = -1
entity.wallpaperId = DEFAULT_WALLPAPER_ID
entity.author = "Yalin"
entity.link = "kinglloy.com"
entity.name = "Rainbow"
return entity
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/CacheWallpaperDataStore.java
================================================
package com.yalin.style.data.repository.datasource;
import com.yalin.style.data.cache.WallpaperCache;
import com.yalin.style.data.entity.WallpaperEntity;
import com.yalin.style.data.exception.ReswitchException;
import com.yalin.style.data.lock.OpenInputStreamLock;
import java.io.InputStream;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/20.
*/
public class CacheWallpaperDataStore implements WallpaperDataStore {
private final WallpaperCache wallpaperCache;
private final OpenInputStreamLock openInputStreamLock;
public CacheWallpaperDataStore(WallpaperCache wallpaperCache,
OpenInputStreamLock openInputStreamLock) {
this.wallpaperCache = wallpaperCache;
this.openInputStreamLock = openInputStreamLock;
}
@Override
public Observable getWallPaperEntity() {
return wallpaperCache.get();
}
@Override
public Observable switchWallPaperEntity() {
if (openInputStreamLock.obtain()) {
openInputStreamLock.release();
return wallpaperCache.getNext();
} else {
return Observable.create(emitter ->
emitter.onError(new ReswitchException()));
}
}
@Override
public Observable openInputStream(String wallpaperId) {
throw new UnsupportedOperationException("Cache data store not support open input stream.");
}
@Override
public Observable getWallpaperCount() {
return wallpaperCache.getWallpaperCount();
}
@Override
public Observable likeWallpaper(String wallpaperId) {
throw new UnsupportedOperationException("Cache data store not support open input stream.");
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/DbWallpaperDataStore.java
================================================
package com.yalin.style.data.repository.datasource;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import com.yalin.style.data.cache.WallpaperCache;
import com.yalin.style.data.entity.WallpaperEntity;
import com.yalin.style.data.exception.LikeException;
import com.yalin.style.data.lock.LikeWallpaperLock;
import com.yalin.style.data.lock.OpenInputStreamLock;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract;
import com.yalin.style.data.repository.datasource.provider.StyleContract.Wallpaper;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Queue;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public class DbWallpaperDataStore implements WallpaperDataStore {
private static final String TAG = "DbWallpaperDataStore";
public static final String DEFAULT_WALLPAPER_ID = "-1";
private final Context context;
private final WallpaperCache wallpaperCache;
private final OpenInputStreamLock openInputStreamLock;
private final LikeWallpaperLock likeWallpaperLock;
public DbWallpaperDataStore(Context context, WallpaperCache wallpaperCache,
OpenInputStreamLock openInputStreamLock,
LikeWallpaperLock likeWallpaperLock) {
this.context = context;
this.wallpaperCache = wallpaperCache;
this.openInputStreamLock = openInputStreamLock;
this.likeWallpaperLock = likeWallpaperLock;
}
@Override
public Observable getWallPaperEntity() {
return createEntitiesObservable().doOnNext(wallpaperCache::put).map(Queue::peek);
}
@Override
public Observable switchWallPaperEntity() {
return getWallPaperEntity();
}
@Override
public Observable openInputStream(String wallpaperId) {
return Observable.create(emitter -> {
try {
openInputStreamLock.obtain();
InputStream inputStream;
if (DEFAULT_WALLPAPER_ID.equals(wallpaperId)) {
inputStream = context.getAssets().open("painterly-architectonic.jpg");
} else {
inputStream = context.getContentResolver().openInputStream(
StyleContract.Wallpaper.buildWallpaperUri(wallpaperId));
}
emitter.onNext(inputStream);
emitter.onComplete();
} catch (IOException e) {
LogUtil.D(TAG, "Open input stream failed for wallpaperId : " + wallpaperId);
emitter.onError(e);
} finally {
openInputStreamLock.release();
}
});
}
@Override
public Observable getWallpaperCount() {
return Observable.create(emitter -> {
Cursor cursor = null;
int count = 0;
ContentResolver contentResolver = context.getContentResolver();
cursor = contentResolver.query(StyleContract.Wallpaper.CONTENT_URI,
null, null, null, null);
if (cursor != null) {
// contain default
count = cursor.getCount() + 1;
cursor.close();
}
emitter.onNext(count);
emitter.onComplete();
});
}
@Override
public Observable likeWallpaper(String wallpaperId) {
if (!likeWallpaperLock.obtain()) {
return Observable.create(emitter -> emitter.onError(new LikeException()));
}
wallpaperCache.likeWallpaper(wallpaperId);
return Observable.create(emitter -> {
Cursor cursor = null;
try {
ContentResolver contentResolver = context.getContentResolver();
cursor = contentResolver.query(StyleContract.Wallpaper.buildWallpaperUri(wallpaperId),
null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
WallpaperEntity entity = WallpaperEntity.readEntityFromCursor(cursor);
entity.liked = !entity.liked;
int columnCount = contentResolver
.update(StyleContract.Wallpaper.buildWallpaperLikeUri(wallpaperId),
buildKeepContentValue(entity), null, null);
if (columnCount > 0) {
emitter.onNext(entity.liked);
} else {
throw new LikeException();
}
} else {
throw new LikeException();
}
} catch (Exception e) {
emitter.onError(e);
} finally {
likeWallpaperLock.release();
if (cursor != null) {
cursor.close();
}
}
});
}
private Observable> createEntitiesObservable() {
return Observable.create(emitter -> {
Cursor cursor = null;
Queue validWallpapers = new LinkedList<>();
try {
ContentResolver contentResolver = context.getContentResolver();
cursor = contentResolver.query(Wallpaper.CONTENT_URI,
null, null, null, null);
validWallpapers.addAll(WallpaperEntity.readCursor(context, cursor));
} finally {
if (cursor != null) {
cursor.close();
}
}
// from db all wallpaper can be liked
for (WallpaperEntity entity : validWallpapers) {
entity.canLike = true;
}
validWallpapers.add(buildDefaultWallpaper());
emitter.onNext(validWallpapers);
emitter.onComplete();
});
}
public static WallpaperEntity buildDefaultWallpaper() {
WallpaperEntity wallpaperEntity = new WallpaperEntity();
wallpaperEntity.id = -1;
wallpaperEntity.attribution = "kinglloy.com";
wallpaperEntity.byline = "Lyubov Popova, 1918";
wallpaperEntity.imageUri = "imageUri";
wallpaperEntity.title = "Painterly Architectonic";
wallpaperEntity.wallpaperId = DEFAULT_WALLPAPER_ID;
wallpaperEntity.liked = false;
wallpaperEntity.isDefault = true;
wallpaperEntity.canLike = false;
return wallpaperEntity;
}
private ContentValues buildKeepContentValue(WallpaperEntity entity) {
ContentValues contentValues = new ContentValues();
contentValues.put(Wallpaper.COLUMN_NAME_LIKED, entity.liked ? 1 : 0);
return contentValues;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/GalleryWallpaperDataStore.kt
================================================
package com.yalin.style.data.repository.datasource
import android.content.ContentProviderOperation
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.text.TextUtils
import android.text.format.DateUtils
import com.fernandocejas.arrow.checks.Preconditions
import com.yalin.style.data.R
import com.yalin.style.data.cache.GalleryWallpaperCache
import com.yalin.style.data.entity.GalleryWallpaperEntity
import com.yalin.style.data.entity.WallpaperEntity
import com.yalin.style.data.exception.ReswitchException
import com.yalin.style.data.extensions.DelegateExt
import com.yalin.style.data.lock.OpenInputStreamLock
import com.yalin.style.data.repository.datasource.io.GalleryWallpapersHandler
import com.yalin.style.data.repository.datasource.io.RemoveGalleryWallpapersHandler
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.repository.datasource.sync.gallery.GalleryScheduleService
import com.yalin.style.data.utils.getCacheFileForUri
import com.yalin.style.data.utils.getImagesFromTreeUri
import com.yalin.style.data.utils.notifyChange
import com.yalin.style.domain.GalleryWallpaper
import io.reactivex.Observable
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.util.*
import kotlin.collections.ArrayList
/**
* @author jinyalin
* @since 2017/5/22.
*/
class GalleryWallpaperDataStore(val context: Context,
val openInputStreamLock: OpenInputStreamLock,
val galleryWallpaperCache: GalleryWallpaperCache) :
WallpaperDataStore {
var currentGalleryWallpaperId: Long by DelegateExt.preferences(context,
GalleryScheduleService.PREF_CURRENT_SHOW_WALLPAPER_ID, -1)
var rotateIntervalMin: Int by DelegateExt.preferences(context,
GalleryScheduleService.PREF_ROTATE_INTERVAL_MIN,
GalleryScheduleService.DEFAULT_ROTATE_INTERVAL_MIN)
override fun getWallPaperEntity(): Observable {
if (galleryWallpaperCache.isCached()) {
val entities = galleryWallpaperCache.get()!!
return Observable.create { emitter ->
emitter.onNext(peekValid(entities))
emitter.onComplete()
}
} else {
return createEntitiesObservable().doOnNext(galleryWallpaperCache::put).map { entities ->
return@map peekValid(entities)
}
}
}
override fun switchWallPaperEntity(): Observable {
if (openInputStreamLock.obtain()) {
openInputStreamLock.release()
return doSwitch()
} else {
return Observable.create {
emitter ->
emitter.onError(ReswitchException())
}
}
}
override fun openInputStream(wallpaperId: String?): Observable {
Preconditions.checkArgument(!TextUtils.isEmpty(wallpaperId))
return Observable.create { emitter ->
var cursor: Cursor? = null
try {
openInputStreamLock.obtain()
var inputStream: InputStream? = null
if (DbWallpaperDataStore.DEFAULT_WALLPAPER_ID == wallpaperId) {
inputStream = context.assets.open("painterly-architectonic.jpg")
} else {
cursor = context.contentResolver.query(
StyleContract.GalleryWallpaper.buildGalleryWallpaperUri(
wallpaperId!!.toLong()), null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val uriString = cursor.getString(cursor.getColumnIndex(
StyleContract.GalleryWallpaper.COLUMN_NAME_CUSTOM_URI))
val isTreeUri = cursor.getInt(cursor.getColumnIndex(
StyleContract.GalleryWallpaper.COLUMN_NAME_IS_TREE_URI)) == 1
if (isTreeUri && Build.VERSION.SDK_INT >= 21) {
val images = getImagesFromTreeUri(context, Uri.parse(uriString),
Int.MAX_VALUE)
inputStream = context.contentResolver.openInputStream(
images[Random().nextInt(images.size)])
} else {
try {
inputStream = context.contentResolver.openInputStream(
Uri.parse(uriString))
} catch (e: Exception) {
// if cached file exist then use cached file
val cacheFile = getCacheFileForUri(context, uriString)
if ((cacheFile != null && cacheFile.exists())) {
inputStream = FileInputStream(cacheFile)
} else {
throw IOException("Cannot open gallery uri : " + uriString)
}
}
}
}
}
emitter.onNext(inputStream!!)
emitter.onComplete()
} catch (e: IOException) {
emitter.onError(e)
} finally {
cursor?.close()
openInputStreamLock.release()
}
}
}
override fun getWallpaperCount(): Observable {
return Observable.create { emitter ->
var totalCount: Int = 0
if (galleryWallpaperCache.isCached()) {
totalCount = galleryWallpaperCache.get()!!.size
} else {
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(StyleContract.GalleryWallpaper.CONTENT_URI,
null, null, null, null)
if (cursor != null) {
totalCount = cursor.count
}
} finally {
cursor?.close()
}
}
emitter.onNext(totalCount)
emitter.onComplete()
}
}
override fun likeWallpaper(wallpaperId: String?): Observable {
throw UnsupportedOperationException("Gallery data store not support like.")
}
fun addGalleryWallpaperUris(uris: List): Observable {
return Observable.create { emitter ->
galleryWallpaperCache.evictAll()
var success = true
try {
val wallpaperHandler = GalleryWallpapersHandler(context, uris)
val contentOperators = ArrayList()
wallpaperHandler.makeContentProviderOperations(contentOperators)
if (contentOperators.size > 0) {
context.contentResolver.applyBatch(StyleContract.AUTHORITY, contentOperators)
}
} catch (e: Exception) {
success = false
emitter.onError(e)
}
if (success) {
emitter.onNext(true)
emitter.onComplete()
GalleryScheduleService.publish(context)
}
}
}
fun removeGalleryWallpaperUris(uris: List): Observable {
return Observable.create { emitter ->
galleryWallpaperCache.evictAll()
var success = true
try {
val removeHandler = RemoveGalleryWallpapersHandler(context, uris)
val contentOperators = ArrayList()
removeHandler.makeContentProviderOperations(contentOperators)
if (contentOperators.size > 0) {
context.contentResolver.applyBatch(StyleContract.AUTHORITY, contentOperators)
}
} catch (e: Exception) {
success = false
emitter.onError(e)
}
if (success) {
emitter.onNext(true)
emitter.onComplete()
GalleryScheduleService.publish(context)
}
}
}
fun getGalleryWallpaperUris(): Observable> {
return createEntitiesObservable().doOnNext(galleryWallpaperCache::put)
}
fun forceNow(wallpaperUri: String): Observable {
return Observable.create { emitter ->
if (galleryWallpaperCache.isCached()) {
for (entity in galleryWallpaperCache.get()!!) {
if (TextUtils.equals(entity.uri, wallpaperUri)) {
currentGalleryWallpaperId = entity.id
break
}
}
} else {
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(StyleContract.GalleryWallpaper.CONTENT_URI,
arrayOf(StyleContract.GalleryWallpaper._ID),
StyleContract.GalleryWallpaper.COLUMN_NAME_CUSTOM_URI + " = ? ",
arrayOf(wallpaperUri), null)
if (cursor != null && cursor.moveToFirst()) {
currentGalleryWallpaperId = cursor.getLong(0)
}
} finally {
cursor?.close()
}
}
emitter.onNext(true)
emitter.onComplete()
notifyChange(context, StyleContract.GalleryWallpaper.CONTENT_URI)
}
}
fun setUpdateIntervalMin(intervalMin: Int): Observable {
return Observable.create { emitter ->
GalleryScheduleService.setInterval(context, intervalMin)
emitter.onNext(true)
emitter.onComplete()
}
}
fun getUpdateIntervalMin(): Observable {
return Observable.create { emitter ->
emitter.onNext(rotateIntervalMin)
emitter.onComplete()
}
}
private fun createEntitiesObservable(): Observable> {
return Observable.create> { emitter ->
var cursor: Cursor? = null
val validWallpapers = ArrayList()
try {
val contentResolver = context.contentResolver
cursor = contentResolver.query(StyleContract.GalleryWallpaper.CONTENT_URI,
null, null, null, null)
validWallpapers.addAll(GalleryWallpaperEntity.readCursor(context, cursor))
} finally {
cursor?.close()
}
emitter.onNext(validWallpapers)
emitter.onComplete()
}
}
private fun peekValid(entities: List): WallpaperEntity {
if (entities.isNotEmpty()) {
if (currentGalleryWallpaperId == -1L) {
val selectedEntity: GalleryWallpaperEntity
if (entities.size == 1) {
selectedEntity = entities[0]
currentGalleryWallpaperId = selectedEntity.id
} else {
val random = Random()
selectedEntity = entities[random.nextInt(entities.size)]
currentGalleryWallpaperId = selectedEntity.id
}
return switchToWallpaperEntity(selectedEntity)
} else {
entities.filter { it.id == currentGalleryWallpaperId }
.forEach { return switchToWallpaperEntity(it) }
}
}
return DbWallpaperDataStore.buildDefaultWallpaper()
}
private fun switchToWallpaperEntity(galleryWallpaperEntity: GalleryWallpaperEntity)
: WallpaperEntity {
val wallpaperEntity = WallpaperEntity()
wallpaperEntity.isDefault = false
wallpaperEntity.canLike = false
wallpaperEntity.title = getTitle(galleryWallpaperEntity.dateTime)
wallpaperEntity.byline = getByline(galleryWallpaperEntity.location)
wallpaperEntity.attribution = "kinglloy.com"
wallpaperEntity.imageUri = galleryWallpaperEntity.uri
wallpaperEntity.wallpaperId = galleryWallpaperEntity.id.toString()
return wallpaperEntity
}
private fun getTitle(dateTime: Long): String {
if (dateTime > 0)
return DateUtils.formatDateTime(context, dateTime,
DateUtils.FORMAT_SHOW_DATE
or DateUtils.FORMAT_SHOW_YEAR
or DateUtils.FORMAT_SHOW_WEEKDAY)
else return context.getString(R.string.gallery_from_gallery)
}
private fun getByline(location: String?): String {
if (location.isNullOrEmpty())
return context.getString(R.string.gallery_touch_to_view)
else return location!!
}
private fun doSwitch(): Observable {
if (galleryWallpaperCache.isCached()) {
return Observable.create { emitter ->
val entities = galleryWallpaperCache.get()
emitter.onNext(peekOne(entities!!))
emitter.onComplete()
}
} else {
return createEntitiesObservable().doOnNext(galleryWallpaperCache::put)
.map { entities ->
return@map peekOne(entities)
}
}
}
private fun peekOne(entities: List): WallpaperEntity {
var selectedEntity: GalleryWallpaperEntity? = null
if (entities.size > 1) {
val random = Random()
while (true) {
selectedEntity = entities[random.nextInt(entities.size)]
if (selectedEntity.id != currentGalleryWallpaperId) {
currentGalleryWallpaperId = selectedEntity.id
break
}
}
} else if (entities.size == 1) {
if (currentGalleryWallpaperId != entities[0].id) {
currentGalleryWallpaperId = entities[0].id
}
selectedEntity = entities[0]
} else {
if (currentGalleryWallpaperId != -1L) {
currentGalleryWallpaperId = -1
}
}
if (selectedEntity != null) {
return switchToWallpaperEntity(selectedEntity)
}
return DbWallpaperDataStore.buildDefaultWallpaper()
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/GalleryWallpaperDataStoreFactory.kt
================================================
package com.yalin.style.data.repository.datasource
import android.content.Context
import com.yalin.style.data.cache.GalleryWallpaperCache
import com.yalin.style.data.lock.OpenInputStreamLock
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/24.
*/
@Singleton
class GalleryWallpaperDataStoreFactory @Inject
constructor(val context: Context,
val openInputStreamLock: OpenInputStreamLock,
val galleryWallpaperCache: GalleryWallpaperCache) {
fun create(): GalleryWallpaperDataStore {
return GalleryWallpaperDataStore(context, openInputStreamLock, galleryWallpaperCache)
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/RemoteAdvanceWallpaperDataStore.kt
================================================
package com.yalin.style.data.repository.datasource
import android.content.ContentProviderOperation
import android.content.ContentResolver
import android.content.Context
import android.content.OperationApplicationException
import android.database.Cursor
import android.os.RemoteException
import com.google.gson.JsonParser
import com.yalin.style.data.R
import com.yalin.style.data.entity.AdvanceWallpaperEntity
import com.yalin.style.data.exception.NoContentException
import com.yalin.style.data.exception.RemoteServerException
import com.yalin.style.data.log.LogUtil
import com.yalin.style.data.repository.datasource.io.AdvanceWallpaperHandler
import com.yalin.style.data.repository.datasource.net.RemoteAdvanceWallpaperFetcher
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.repository.datasource.sync.account.Account
import com.yalin.style.domain.interactor.DefaultObserver
import io.reactivex.Observable
/**
* @author jinyalin
* @since 2017/7/31.
*/
class RemoteAdvanceWallpaperDataStore(val context: Context,
val localDataStore: AdvanceWallpaperDataStoreImpl)
: AdvanceWallpaperDataStore {
companion object {
val TAG = "RemoteAdvanceWallpaper"
}
val wallpaperHandler = AdvanceWallpaperHandler(context)
override fun getWallpaperEntity(): AdvanceWallpaperEntity {
throw UnsupportedOperationException("Remote data store not support get wallpaper.")
}
override fun getAdvanceWallpapers(): Observable> {
return Observable.create { emitter ->
val account = Account.getAccount()
val authority = context.getString(R.string.authority)
ContentResolver.cancelSync(account, authority)
val batch = ArrayList()
try {
val wallpapers = RemoteAdvanceWallpaperFetcher(context).fetchDataIfNewer()
val parser = JsonParser()
val handler = AdvanceWallpaperHandler(context)
handler.process(parser.parse(wallpapers))
handler.makeContentProviderOperations(batch)
} catch (e: Exception) {
emitter.onError(RemoteServerException())
return@create
}
try {
val operations = batch.size
if (operations > 0) {
context.contentResolver.applyBatch(StyleContract.AUTHORITY, batch)
}
} catch (ex: RemoteException) {
LogUtil.D(TAG, "RemoteException while applying content provider operations.")
throw RuntimeException("Error executing content provider batch operation", ex)
} catch (ex: OperationApplicationException) {
LogUtil.D(TAG, "OperationApplicationException while applying content provider operations.")
throw RuntimeException("Error executing content provider batch operation", ex)
}
var cursor: Cursor? = null
val validWallpapers = ArrayList()
try {
val contentResolver = context.contentResolver
cursor = contentResolver.query(StyleContract.AdvanceWallpaper.CONTENT_URI,
null, null, null, null)
validWallpapers.addAll(AdvanceWallpaperEntity.readCursor(cursor))
} finally {
if (cursor != null) {
cursor.close()
}
}
if (validWallpapers.isEmpty()) {
emitter.onError(NoContentException())
} else {
emitter.onNext(validWallpapers)
}
emitter.onComplete()
}
}
override fun selectWallpaper(wallpaperId: String, tempSelect: Boolean): Observable {
throw UnsupportedOperationException("Remote data store not support select wallpaper.")
}
override fun downloadWallpaper(wallpaperId: String): Observable {
return Observable.create { emitter ->
val entity = localDataStore.loadWallpaperEntity(wallpaperId)
wallpaperHandler.downloadWallpaperComponent(entity, object : DefaultObserver() {
override fun onNext(downloadedLength: Long) {
emitter.onNext(downloadedLength)
}
override fun onComplete() {
emitter.onComplete()
}
override fun onError(exception: Throwable) {
emitter.onError(exception)
}
})
}
}
override fun readAd(wallpaperId: String): Observable {
throw UnsupportedOperationException("Remote data store not support read wallpaper ad.")
}
override fun rollback() {
// do nothing
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/SourcesDataStore.kt
================================================
package com.yalin.style.data.repository.datasource
import com.yalin.style.data.entity.SourceEntity
import io.reactivex.Observable
/**
* @author jinyalin
* @since 2017/5/23.
*/
interface SourcesDataStore {
fun getSources(): Observable>
fun selectSource(sourceId: Int, tempSelect: Boolean): Observable
fun getUsedSourceId(): Int
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/SourcesDataStoreFactory.kt
================================================
package com.yalin.style.data.repository.datasource
import android.content.Context
import com.yalin.style.data.cache.SourcesCache
import com.yalin.style.data.lock.SelectSourceLock
import javax.inject.Inject
import javax.inject.Singleton
/**
* @author jinyalin
* @since 2017/5/23.
*/
@Singleton
class SourcesDataStoreFactory @Inject
constructor(val context: Context,
val sourcesCache: SourcesCache,
val selectSourceLock: SelectSourceLock) {
fun create(): SourcesDataStore {
return SourcesDataStoreImpl(context, sourcesCache, selectSourceLock)
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/SourcesDataStoreImpl.kt
================================================
package com.yalin.style.data.repository.datasource
import android.content.Context
import com.yalin.style.data.cache.SourcesCache
import com.yalin.style.data.entity.SourceEntity
import com.yalin.style.data.lock.SelectSourceLock
import io.reactivex.Observable
/**
* @author jinyalin
* @since 2017/5/23.
*/
class SourcesDataStoreImpl(val context: Context,
val sourcesCache: SourcesCache,
val selectSourceLock: SelectSourceLock) : SourcesDataStore {
override fun selectSource(sourceId: Int, tempSelect: Boolean): Observable {
return Observable.create { emitter ->
try {
if ((!tempSelect && selectSourceLock.obtain()) || tempSelect) {
emitter.onNext(sourcesCache.selectSource(sourceId, tempSelect))
} else {
emitter.onNext(false)
}
emitter.onComplete()
} finally {
selectSourceLock.release()
}
}
}
override fun getSources(): Observable> {
return sourcesCache.getSources(context)
}
override fun getUsedSourceId() = sourcesCache.getUsedSourceId()
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/StyleWallpaperDataStoreFactory.java
================================================
package com.yalin.style.data.repository.datasource;
import android.content.Context;
import com.yalin.style.data.cache.WallpaperCache;
import com.yalin.style.data.lock.LikeWallpaperLock;
import com.yalin.style.data.lock.OpenInputStreamLock;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* @author jinyalin
* @since 2017/4/18.
*/
@Singleton
public class StyleWallpaperDataStoreFactory {
private final Context context;
private final WallpaperCache wallpaperCache;
private final OpenInputStreamLock openInputStreamLock;
private final LikeWallpaperLock likeWallpaperLock;
@Inject
StyleWallpaperDataStoreFactory(Context context, WallpaperCache wallpaperCache,
OpenInputStreamLock openInputStreamLock,
LikeWallpaperLock likeWallpaperLock) {
this.context = context;
this.wallpaperCache = wallpaperCache;
this.openInputStreamLock = openInputStreamLock;
this.likeWallpaperLock = likeWallpaperLock;
}
public WallpaperDataStore create() {
WallpaperDataStore wallpaperDataStore;
if (!wallpaperCache.isDirty() && wallpaperCache.isCached()) {
wallpaperDataStore = new CacheWallpaperDataStore(wallpaperCache, openInputStreamLock);
} else {
wallpaperDataStore = createDbDataStore();
}
return wallpaperDataStore;
}
public WallpaperDataStore createDbDataStore() {
return new DbWallpaperDataStore(context, wallpaperCache,
openInputStreamLock, likeWallpaperLock);
}
public void onDataRefresh() {
wallpaperCache.evictAll();
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/WallpaperDataStore.java
================================================
package com.yalin.style.data.repository.datasource;
import com.yalin.style.data.entity.WallpaperEntity;
import java.io.InputStream;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public interface WallpaperDataStore {
Observable getWallPaperEntity();
Observable switchWallPaperEntity();
Observable openInputStream(String wallpaperId);
Observable getWallpaperCount();
Observable likeWallpaper(String wallpaperId);
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/io/AdvanceWallpaperHandler.kt
================================================
package com.yalin.style.data.repository.datasource.io
import android.content.ContentProviderOperation
import android.content.Context
import android.database.Cursor
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.yalin.style.data.SyncConfig
import com.yalin.style.data.entity.AdvanceWallpaperEntity
import com.yalin.style.data.exception.NetworkConnectionException
import com.yalin.style.data.exception.RemoteServerException
import com.yalin.style.data.log.LogUtil
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.repository.datasource.provider.StyleContractHelper
import com.yalin.style.data.utils.WallpaperFileHelper
import com.yalin.style.domain.interactor.DefaultObserver
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.*
import java.net.URL
import java.util.ArrayList
import java.util.HashSet
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
/**
* @author jinyalin
* @since 2017/7/28.
*/
class AdvanceWallpaperHandler(context: Context) : JSONHandler(context) {
companion object {
val TAG = "AdvanceWallpaperHandler"
val downloadLock = ReentrantLock()
}
private var wallpapers: ArrayList = ArrayList()
override fun makeContentProviderOperations(list: ArrayList) {
val uri = StyleContractHelper.setUriAsCalledFromSyncAdapter(
StyleContract.AdvanceWallpaper.CONTENT_URI)
list.add(ContentProviderOperation.newDelete(uri).build())
val validFiles = HashSet()
val selectedEntities = querySelectedWallpapers()
validFiles.addAll(getWallpaperNameSet(selectedEntities))
for (wallpaper in this.wallpapers) {
wallpaper.storePath = makeStorePath(wallpaper)
if (!selectedEntities.contains(wallpaper)) {
if (wallpaper.lazyDownload || (downloadWallpaperComponent(wallpaper)
&& WallpaperFileHelper.ensureChecksumValid(mContext,
wallpaper.checkSum, wallpaper.storePath))) {
LogUtil.D(TAG, "download wallpaper component "
+ " success, do output wallpaper.")
outputWallpaper(wallpaper, list)
validFiles.add(makeFilename(wallpaper))
}
}
}
// delete old wallpapers
WallpaperFileHelper.deleteOldComponent(mContext, validFiles)
}
override fun process(element: JsonElement) {
val wallpapers = Gson().fromJson(element, Array::class.java)
this.wallpapers.ensureCapacity(wallpapers.size)
this.wallpapers.addAll(wallpapers)
}
private fun makeFilename(wallpaper: AdvanceWallpaperEntity): String {
return wallpaper.hashCode().toString() + "_component.apk"
}
private fun makeStorePath(wallpaper: AdvanceWallpaperEntity): String {
val outputDir = WallpaperFileHelper.getAdvanceWallpaperDir(mContext)
return File(outputDir, makeFilename(wallpaper)).absolutePath
}
private fun getWallpaperNameSet(entities: List): Set {
val ids = HashSet()
for (entity in entities) {
ids.add(makeFilename(entity))
}
return ids
}
private fun outputWallpaper(wallpaper: AdvanceWallpaperEntity,
list: ArrayList) {
val uri = StyleContractHelper.setUriAsCalledFromSyncAdapter(
StyleContract.AdvanceWallpaper.CONTENT_URI)
val builder = ContentProviderOperation.newInsert(uri)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_WALLPAPER_ID, wallpaper.wallpaperId)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_AUTHOR, wallpaper.author)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_DOWNLOAD_URL, wallpaper.downloadUrl)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_ICON_URL, wallpaper.iconUrl)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_LINK, wallpaper.link)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_NAME, wallpaper.name)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_CHECKSUM, wallpaper.checkSum)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_STORE_PATH, wallpaper.storePath)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_PROVIDER_NAME, wallpaper.providerName)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_SELECTED, 0)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_LAZY_DOWNLOAD,
if (wallpaper.lazyDownload) 1 else 0)
builder.withValue(StyleContract.AdvanceWallpaper.COLUMN_NAME_NEED_AD,
if (wallpaper.needAd) 1 else 0)
list.add(builder.build())
}
private fun querySelectedWallpapers(): List {
var cursor: Cursor? = null
try {
cursor = mContext.contentResolver.query(
StyleContract.AdvanceWallpaper.CONTENT_SELECTED_URI, null, null, null, null)
return AdvanceWallpaperEntity.readCursor(cursor)
} finally {
if (cursor != null) {
cursor.close()
}
}
}
private fun downloadWallpaperComponent(wallpaper: AdvanceWallpaperEntity): Boolean {
return downloadWallpaperComponent(wallpaper, null)
}
fun downloadWallpaperComponent(wallpaper: AdvanceWallpaperEntity,
observer: DefaultObserver?): Boolean {
observer?.onNext(0)
LogUtil.D(TAG, "Start download wallpaper component to " + wallpaper.storePath)
val outputFile = File(wallpaper.storePath)
if (outputFile.exists()) {
if (WallpaperFileHelper.ensureChecksumValid(mContext,
wallpaper.checkSum, wallpaper.storePath)) {
observer?.onComplete()
return true
}
}
synchronized(downloadLock) {
var os: OutputStream? = null
var _is: InputStream? = null
try {
if (outputFile.exists()) {
if (WallpaperFileHelper.ensureChecksumValid(mContext,
wallpaper.checkSum, wallpaper.storePath)) {
observer?.onComplete()
return true
} else {
outputFile.delete()
}
}
val storePath = outputFile.parentFile
storePath.mkdirs()
os = FileOutputStream(outputFile)
val httpClient = OkHttpClient.Builder()
.connectTimeout(SyncConfig.DEFAULT_CONNECT_TIMEOUT.toLong(), TimeUnit.SECONDS)
.readTimeout(SyncConfig.DEFAULT_DOWNLOAD_TIMEOUT.toLong(), TimeUnit.SECONDS)
.build()
val request = Request.Builder().url(URL(wallpaper.downloadUrl)).build()
val response = httpClient.newCall(request).execute()
val responseCode = response.code()
if (responseCode in 200..299) {
_is = response.body().byteStream()
val buffer = ByteArray(1024)
var bytesRead: Int
var writeLength = 0L
bytesRead = _is.read(buffer)
while (bytesRead > 0) {
os.write(buffer, 0, bytesRead)
writeLength += bytesRead
observer?.onNext(writeLength)
bytesRead = _is.read(buffer)
}
os.flush()
observer?.onComplete()
return true
} else {
LogUtil.E(TAG, "Download wallpaper component " + wallpaper.name + " failed.")
observer?.onError(RemoteServerException())
return false
}
} catch (e: IOException) {
e.printStackTrace()
observer?.onError(NetworkConnectionException())
LogUtil.E(TAG, "Download wallpaper component" + wallpaper.name + " failed.", e)
return false
} finally {
ensureChecksum(outputFile, wallpaper.checkSum)
try {
os?.close()
_is?.close()
} catch (e: IOException) {
// ignore
}
}
}
}
private fun ensureChecksum(file: File, checkSum: String) {
if (file.exists()) {
if (!WallpaperFileHelper.ensureChecksumValid(mContext,
checkSum, file.absolutePath)) {
file.delete()
}
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/io/GalleryWallpapersHandler.kt
================================================
package com.yalin.style.data.repository.datasource.io
import android.annotation.SuppressLint
import android.content.ContentProviderOperation
import android.content.Context
import android.location.Address
import android.location.Geocoder
import android.net.Uri
import android.support.media.ExifInterface
import android.text.TextUtils
import com.google.gson.JsonElement
import com.yalin.style.data.log.LogUtil
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.utils.isTreeUri
import com.yalin.style.data.utils.processUriPermission
import com.yalin.style.domain.GalleryWallpaper
import java.text.SimpleDateFormat
import java.util.ArrayList
/**
* @author jinyalin
* @since 2017/5/24.
*/
class GalleryWallpapersHandler(val context: Context,
val uris: List) : JSONHandler(context) {
companion object {
private val TAG = "GalleryWallpapersHandler"
@SuppressLint("SimpleDateFormat")
private val sExifDateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss")
}
private var mGeocoder: Geocoder = Geocoder(context)
override fun makeContentProviderOperations(list: ArrayList) {
val uri = StyleContract.GalleryWallpaper.CONTENT_URI
for (wallpaperEntity in uris) {
if (TextUtils.isEmpty(wallpaperEntity.uri)) {
continue
}
wallpaperEntity.isTreeUri = isTreeUri(Uri.parse(wallpaperEntity.uri))
processUriPermission(context, wallpaperEntity)
val builder = ContentProviderOperation.newInsert(uri)
builder.withValue(StyleContract.GalleryWallpaper.COLUMN_NAME_CUSTOM_URI,
wallpaperEntity.uri)
builder.withValue(StyleContract.GalleryWallpaper.COLUMN_NAME_IS_TREE_URI,
if (wallpaperEntity.isTreeUri) 1 else 0)
readMetaData(wallpaperEntity.uri, builder)
list.add(builder.build())
}
}
override fun process(element: JsonElement) {
}
private fun readMetaData(uriString: String, builder: ContentProviderOperation.Builder) {
try {
val uri = Uri.parse(uriString)
var hasMetadata = false
context.contentResolver.openInputStream(uri).use({ `in` ->
if (`in` == null) {
return
}
val exifInterface = ExifInterface(`in`)
val dateString = exifInterface.getAttribute(ExifInterface.TAG_DATETIME)
if (!TextUtils.isEmpty(dateString)) {
val date = sExifDateFormat.parse(dateString)
builder.withValue(StyleContract.GalleryWallpaper.COLUMN_NAME_DATE_TIME,
date.time)
hasMetadata = true
}
val latlong = exifInterface.latLong
if (latlong != null) {
// Reverse geocode
var addresses: List? = null
try {
addresses = mGeocoder.getFromLocation(latlong[0], latlong[1], 1)
} catch (e: IllegalArgumentException) {
LogUtil.E(TAG, "Invalid latitude/longitude, skipping location metadata", e)
}
if (addresses != null && addresses.isNotEmpty()) {
val addr = addresses[0]
val locality = addr.locality
val adminArea = addr.adminArea
val countryCode = addr.countryCode
val sb = StringBuilder()
if (!TextUtils.isEmpty(locality)) {
sb.append(locality)
}
if (!TextUtils.isEmpty(adminArea)) {
if (sb.isNotEmpty()) {
sb.append(", ")
}
sb.append(adminArea)
}
if (!TextUtils.isEmpty(countryCode)) {
if (sb.isNotEmpty()) {
sb.append(", ")
}
sb.append(countryCode)
}
builder.withValue(StyleContract.GalleryWallpaper.COLUMN_NAME_LOCATION,
sb.toString())
hasMetadata = true
}
}
})
if (hasMetadata) {
builder.withValue(StyleContract.GalleryWallpaper.COLUMN_NAME_HAS_METADATA,
if (hasMetadata) 1 else 0)
}
} catch (e: Exception) {
LogUtil.E(TAG, "Couldn't read image metadata.", e)
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/io/JSONHandler.java
================================================
package com.yalin.style.data.repository.datasource.io;
import android.content.ContentProviderOperation;
import android.content.Context;
import com.google.gson.JsonElement;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
/**
* YaLin 2017/1/3.
*/
public abstract class JSONHandler {
protected Context mContext;
public JSONHandler(Context context) {
mContext = context;
}
public abstract void makeContentProviderOperations(ArrayList list);
public abstract void process(JsonElement element);
/**
* Loads a raw resource and returns its content as a String.
*
* @throws IOException If any error was encountered, such as an incorrect resource ID, or
* inaccessible file.
*/
public static String parseResource(Context context, int resource) throws IOException {
InputStream stream = null;
String data;
try {
stream = context.getResources().openRawResource(resource);
data = parseStream(stream);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore exceptions during stream close, other exceptions thrown earlier will
// be handled by the calling methods
}
}
}
return data;
}
private static String parseStream(final InputStream stream) throws IOException {
Reader reader = null;
Writer writer = new StringWriter();
char[] buffer = new char[1024];
// IO errors are passed up to the calling method and must be caught there.
try {
reader = new BufferedReader(new InputStreamReader(stream, Charset.forName("UTF-8")));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// Ignore exceptions during stream close, other exceptions thrown earlier will
// be handled by the calling methods
}
}
}
return writer.toString();
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/io/RemoveGalleryWallpapersHandler.kt
================================================
package com.yalin.style.data.repository.datasource.io
import android.content.ContentProviderOperation
import android.content.Context
import android.text.TextUtils
import com.google.gson.JsonElement
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.domain.GalleryWallpaper
import java.util.ArrayList
/**
* YaLin
* On 2017/5/26.
*/
class RemoveGalleryWallpapersHandler(val context: Context,
val uris: List) : JSONHandler(context) {
override fun makeContentProviderOperations(list: ArrayList) {
uris.filterNot { TextUtils.isEmpty(it.uri) }
.map {
StyleContract.GalleryWallpaper
.buildGalleryWallpaperDeleteUri(it.uri)
}
.map { ContentProviderOperation.newDelete(it) }
.mapTo(list) { it.build() }
}
override fun process(element: JsonElement) {
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/io/WallpapersHandler.java
================================================
package com.yalin.style.data.repository.datasource.io;
import android.content.ContentProviderOperation;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.yalin.style.data.SyncConfig;
import com.yalin.style.data.entity.WallpaperEntity;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract.Wallpaper;
import com.yalin.style.data.repository.datasource.provider.StyleContractHelper;
import com.yalin.style.data.utils.TimeUtil;
import com.yalin.style.data.utils.WallpaperFileHelper;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* YaLin 2017/1/3.
*/
public class WallpapersHandler extends JSONHandler {
private static final String TAG = "WallpapersHandler";
private ArrayList mWallpapers = new ArrayList<>();
public WallpapersHandler(Context context) {
super(context);
}
@Override
public void makeContentProviderOperations(ArrayList list) {
Uri uri = StyleContractHelper.setUriAsCalledFromSyncAdapter(Wallpaper.CONTENT_URI);
list.add(ContentProviderOperation.newDelete(uri).build());
Set validFiles = new HashSet<>();
List keepedEntity = queryKeepedWallpapers();
validFiles.addAll(getWallpaperIdSet(keepedEntity));
for (WallpaperEntity wallpaper : mWallpapers) {
Uri wallpaperUri = Wallpaper.buildWallpaperSaveUri(wallpaper.wallpaperId);
if (!keepedEntity.contains(wallpaper) && downloadWallpaper(wallpaper, wallpaperUri)
&& WallpaperFileHelper.ensureWallpaperChecksumValid(mContext,
wallpaper.checksum, wallpaper.wallpaperId)) {
LogUtil.D(TAG, "download wallpaper " + wallpaperUri
+ " success, do output wallpaper.");
outputWallpaper(wallpaper, list, wallpaperUri.toString());
validFiles.add(wallpaper.wallpaperId);
}
}
// delete old wallpapers
WallpaperFileHelper.deleteOldWallpapers(mContext, validFiles);
}
@Override
public void process(JsonElement element) {
WallpaperEntity[] wallpapers = new Gson().fromJson(element, WallpaperEntity[].class);
mWallpapers.ensureCapacity(wallpapers.length);
Collections.addAll(mWallpapers, wallpapers);
}
private Set getWallpaperIdSet(List entities) {
Set ids = new HashSet<>();
for (WallpaperEntity entity : entities) {
ids.add(entity.wallpaperId);
}
return ids;
}
private List queryKeepedWallpapers() {
Cursor cursor = null;
try {
cursor = mContext.getContentResolver().query(Wallpaper.CONTENT_KEEPED_URI,
null, null, null, null);
return WallpaperEntity.readCursor(mContext, cursor);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
private void outputWallpaper(WallpaperEntity wallpaper,
ArrayList list, String uriString) {
Uri uri = StyleContractHelper.setUriAsCalledFromSyncAdapter(Wallpaper.CONTENT_URI);
ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(uri);
builder.withValue(Wallpaper.COLUMN_NAME_WALLPAPER_ID, wallpaper.wallpaperId);
builder.withValue(Wallpaper.COLUMN_NAME_TITLE, wallpaper.title);
builder.withValue(Wallpaper.COLUMN_NAME_IMAGE_URI, uriString);
builder.withValue(Wallpaper.COLUMN_NAME_BYLINE, wallpaper.byline);
builder.withValue(Wallpaper.COLUMN_NAME_ATTRIBUTION, wallpaper.attribution);
builder.withValue(Wallpaper.COLUMN_NAME_ADD_DATE, TimeUtil.getCurrentTime(mContext));
builder.withValue(Wallpaper.COLUMN_NAME_LIKED, wallpaper.liked ? 1 : 0);
builder.withValue(Wallpaper.COLUMN_NAME_CHECKSUM, wallpaper.checksum);
list.add(builder.build());
}
private boolean downloadWallpaper(WallpaperEntity wallpaper, Uri uri) {
LogUtil.D(TAG, "Start download wallpaper uri = " + uri);
OutputStream os = null;
InputStream is = null;
try {
os = mContext.getContentResolver().openOutputStream(uri);
if (os == null) {
return false;
}
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(SyncConfig.DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(SyncConfig.DEFAULT_DOWNLOAD_TIMEOUT, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder().url(new URL(wallpaper.imageUri)).build();
Response response = httpClient.newCall(request).execute();
int responseCode = response.code();
if (responseCode >= 200 && responseCode < 300) {
is = response.body().byteStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) > 0) {
os.write(buffer, 0, bytesRead);
}
os.flush();
return true;
} else {
LogUtil.E(TAG, "Download wallpaper " + wallpaper.title + " failed.");
return false;
}
} catch (IOException e) {
e.printStackTrace();
LogUtil.E(TAG, "Download wallpaper " + wallpaper.title + " failed.", e);
return false;
} finally {
try {
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
// ignore
}
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/net/DataFetcher.java
================================================
package com.yalin.style.data.repository.datasource.net;
import android.content.Context;
import android.text.TextUtils;
import com.yalin.style.data.BuildConfig;
import com.yalin.style.data.SyncConfig;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.utils.HttpRequestUtil;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* @author jinyalin
* @since 2017/7/28.
*/
public abstract class DataFetcher {
private static final String TAG = "DataFetcher";
private final Context mContext;
private final String mWallpaperUrl;
public DataFetcher(Context context) {
mContext = context;
mWallpaperUrl = getUrl();
}
public String fetchDataIfNewer() throws IOException {
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(SyncConfig.DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(SyncConfig.DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url(new URL(mWallpaperUrl))
.post(RequestBody.create(null, HttpRequestUtil.getRequestBody(mContext)))
.build();
Response response = httpClient.newCall(request).execute();
if (response == null) {
LogUtil.F(TAG, "Request for wallpaper returned null response.");
throw new IOException("Request for data wallpaper returned null response.");
}
int status = response.code();
if (status == HttpURLConnection.HTTP_OK) {
LogUtil.D(TAG, "Server return HTTP_OK, so new data is available.");
String body = response.body().string();
if (TextUtils.isEmpty(body)) {
LogUtil.F(TAG, "Request for wallpaper returned empty data.");
throw new IOException("Error fetching wallpaper data : no data.");
}
LogUtil.D(TAG, "Wallpaper " + mWallpaperUrl + " read, contents: " + body);
return body;
} else if (status == HttpURLConnection.HTTP_NOT_MODIFIED) {
LogUtil.D(TAG, "HTTP_NOT_MODIFIED: data has not changed since");
return null;
} else {
LogUtil.D(TAG, "Error fetching conference data: HTTP status " + status + " and manifest " +
mWallpaperUrl);
throw new IOException("Error fetching conference data: HTTP status " + status);
}
}
protected abstract String getUrl();
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/net/RemoteAdvanceWallpaperFetcher.java
================================================
package com.yalin.style.data.repository.datasource.net;
import android.content.Context;
import com.yalin.style.data.BuildConfig;
/**
* @author jinyalin
* @since 2017/7/28.
*/
public class RemoteAdvanceWallpaperFetcher extends DataFetcher {
public RemoteAdvanceWallpaperFetcher(Context context) {
super(context);
}
protected String getUrl() {
return BuildConfig.SERVER_WALLPAPER_ENDPOINT + "/advance";
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/provider/StyleContract.java
================================================
package com.yalin.style.data.repository.datasource.provider;
import android.net.Uri;
import android.provider.BaseColumns;
/**
* YaLin 2016/12/30.
*/
public class StyleContract {
public static final String AUTHORITY = "com.yalin.style";
private static final String SCHEME = "content://";
interface WallpaperColumns {
/**
* Type: TEXT
*/
String COLUMN_NAME_WALLPAPER_ID = "wallpaper_id";
/**
* Type: TEXT
*/
String COLUMN_NAME_IMAGE_URI = "image_uri";
/**
* Type: TEXT
*/
String COLUMN_NAME_TITLE = "title";
/**
* Type: TEXT
*/
String COLUMN_NAME_BYLINE = "byline";
/**
* Type: TEXT
*/
String COLUMN_NAME_ATTRIBUTION = "attribution";
/**
* Type: long
*/
String COLUMN_NAME_ADD_DATE = "add_date";
/**
* Type: SHORT
*/
String COLUMN_NAME_LIKED = "keep";
/**
* Type: TEXT
*/
String COLUMN_NAME_CHECKSUM = "checksum";
}
interface GalleryColumns {
/**
* Type: TEXT
*/
String COLUMN_NAME_CUSTOM_URI = "custom_wallpaper_uri";
/**
* Type: INTEGER
*/
String COLUMN_NAME_IS_TREE_URI = "is_tree_uri";
/**
* Type: INTEGER
*/
String COLUMN_NAME_DATE_TIME = "date_time";
/**
* Type: TEXT
*/
String COLUMN_NAME_LOCATION = "location";
/**
* Type: INTEGER
*/
String COLUMN_NAME_HAS_METADATA = "has_metadata";
}
interface AdvanceWallpaperColumns {
/**
* Type: TEXT
*/
String COLUMN_NAME_WALLPAPER_ID = "wallpaper_id";
/**
* Type: TEXT
*/
String COLUMN_NAME_ICON_URL = "icon_url";
/**
* Type: TEXT
*/
String COLUMN_NAME_NAME = "name";
/**
* Type: TEXT
*/
String COLUMN_NAME_LINK = "link";
/**
* Type: TEXT
*/
String COLUMN_NAME_AUTHOR = "author";
/**
* Type: TEXT
*/
String COLUMN_NAME_DOWNLOAD_URL = "download_url";
/**
* Type: TEXT
*/
String COLUMN_NAME_CHECKSUM = "checksum";
/**
* Type: TEXT
*/
String COLUMN_NAME_STORE_PATH = "store_path";
/**
* Type: TEXT
*/
String COLUMN_NAME_PROVIDER_NAME = "provider_name";
/**
* Type: INTEGER
*/
String COLUMN_NAME_SELECTED = "selected";
/**
* Type: INTEGER
*/
String COLUMN_NAME_LAZY_DOWNLOAD = "lazy_download";
/**
* Type: INTEGER
*/
String COLUMN_NAME_NEED_AD = "need_ad";
}
public static final Uri BASE_CONTENT_URI = Uri.parse(SCHEME + AUTHORITY);
private static final String PATH_SOURCE = "source";
private static final String PATH_WALLPAPER = "wallpaper";
private static final String PATH_GALLERY = "gallery";
private static final String PATH_ADVANCE_WALLPAPER = "advance_wallpaper";
public static final String[] TOP_LEVEL_PATHS = {
PATH_WALLPAPER
};
public static final class Source {
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_SOURCE).build();
}
public static final class Wallpaper implements WallpaperColumns, BaseColumns {
public static final String TABLE_NAME = "wallpaper";
public static final String PATH_LIKE_WALLPAPER = "like";
public static final String PATH_LIKED_WALLPAPER = "liked";
public static final String PATH_SAVE_WALLPAPER = "save";
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_WALLPAPER).build();
public static final Uri CONTENT_KEEPED_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_WALLPAPER)
.appendPath(PATH_LIKED_WALLPAPER).build();
public static Uri buildWallpaperUri(String wallpaperId) {
return CONTENT_URI.buildUpon().appendPath(wallpaperId).build();
}
public static Uri buildWallpaperSaveUri(String wallpaperId) {
return CONTENT_URI.buildUpon()
.appendPath(PATH_SAVE_WALLPAPER).appendPath(wallpaperId).build();
}
public static Uri buildWallpaperLikeUri(String wallpaperId) {
return CONTENT_URI.buildUpon()
.appendPath(PATH_LIKE_WALLPAPER).appendPath(wallpaperId).build();
}
public static String getWallpaperId(Uri uri) {
return uri.getPathSegments().get(1);
}
public static String getWallpaperSaveId(Uri uri) {
return uri.getPathSegments().get(2);
}
public static String getWallpaperLikeId(Uri uri) {
return uri.getPathSegments().get(2);
}
}
public static final class GalleryWallpaper implements GalleryColumns, BaseColumns {
public static final String TABLE_NAME = "gallery_wallpaper";
public static final String PATH_URI = "uri";
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_GALLERY).build();
public static Uri buildGalleryWallpaperUri(long insertId) {
return CONTENT_URI.buildUpon().appendPath(String.valueOf(insertId)).build();
}
public static String getGalleryWallpaperId(Uri uri) {
return uri.getPathSegments().get(1);
}
public static Uri buildGalleryWallpaperDeleteUri(String uri) {
return CONTENT_URI.buildUpon().appendPath(PATH_URI)
.appendPath(String.valueOf(uri)).build();
}
public static String getGalleryWallpaperDeleteUri(Uri uri) {
return uri.getPathSegments().get(2);
}
}
public static final class AdvanceWallpaper implements AdvanceWallpaperColumns, BaseColumns {
public static final String TABLE_NAME = "advance_wallpaper";
public static final String PATH_SELECTED_WALLPAPER = "selected";
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendEncodedPath(PATH_ADVANCE_WALLPAPER).build();
public static final Uri CONTENT_SELECTED_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_ADVANCE_WALLPAPER)
.appendPath(PATH_SELECTED_WALLPAPER).build();
public static Uri buildWallpaperUri(String wallpaperId) {
return CONTENT_URI.buildUpon().appendPath(wallpaperId).build();
}
public static String getWallpaperId(Uri uri) {
return uri.getPathSegments().get(1);
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/provider/StyleContractHelper.java
================================================
package com.yalin.style.data.repository.datasource.provider;
import android.net.Uri;
/**
* YaLin 2017/1/3.
*/
public class StyleContractHelper {
private static final String QUERY_PARAMETER_CALLER_IS_SYNC_ADAPTER = "callerIsSyncAdapter";
public static Uri setUriAsCalledFromSyncAdapter(Uri uri) {
return uri.buildUpon().appendQueryParameter(QUERY_PARAMETER_CALLER_IS_SYNC_ADAPTER, "true")
.build();
}
public static boolean isUriCalledFromSyncAdapter(Uri uri) {
return uri.getBooleanQueryParameter(QUERY_PARAMETER_CALLER_IS_SYNC_ADAPTER, false);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/provider/StyleDatabase.java
================================================
package com.yalin.style.data.repository.datasource.provider;
import android.content.ContentResolver;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import com.yalin.style.data.R;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract.AdvanceWallpaper;
import com.yalin.style.data.repository.datasource.provider.StyleContract.Wallpaper;
import com.yalin.style.data.repository.datasource.sync.account.Account;
/**
* YaLin 2016/12/30.
*/
public class StyleDatabase extends SQLiteOpenHelper {
private static final String TAG = "StyleDatabase";
private static final String DATABASE_NAME = "style.db";
private static final int VERSION_2016_12_30 = 1;
private static final int VERSION_2017_4_30 = 2;
private static final int VERSION_2017_5_24 = 3;
private static final int VERSION_2017_7_28 = 4;
private static final int VERSION_2017_8_11 = 5;
private static final int CUR_DATABASE_VERSION = VERSION_2017_8_11;
private final Context mContext;
interface Tables {
String WALLPAPER = StyleContract.Wallpaper.TABLE_NAME;
String GALLERY = StyleContract.GalleryWallpaper.TABLE_NAME;
String ADVANCE_WALLPAPER = AdvanceWallpaper.TABLE_NAME;
}
public StyleDatabase(Context context) {
super(context, DATABASE_NAME, null, CUR_DATABASE_VERSION);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.WALLPAPER + " ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ StyleContract.Wallpaper.COLUMN_NAME_WALLPAPER_ID + " TEXT,"
+ StyleContract.Wallpaper.COLUMN_NAME_TITLE + " TEXT,"
+ StyleContract.Wallpaper.COLUMN_NAME_IMAGE_URI + " TEXT,"
+ StyleContract.Wallpaper.COLUMN_NAME_ATTRIBUTION + " TEXT,"
+ StyleContract.Wallpaper.COLUMN_NAME_BYLINE + " TEXT,"
+ StyleContract.Wallpaper.COLUMN_NAME_ADD_DATE + " INTEGER);");
upgradeFrom20161230to20170430(db);
upgradeFrom20170430to20170524(db);
upgradeFrom20170525to20170728(db);
upgradeFrom20170728to20170811(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
LogUtil.D(TAG, "onUpgrade() from " + oldVersion + " to " + newVersion);
// Cancel any sync currently in progress
android.accounts.Account account = Account.getAccount();
if (account != null) {
LogUtil.D(TAG, "Cancelling any pending syncs for account");
ContentResolver.cancelSync(account, mContext.getString(R.string.authority));
}
int version = oldVersion;
if (version == VERSION_2016_12_30) {
upgradeFrom20161230to20170430(db);
version = VERSION_2017_4_30;
}
if (version == VERSION_2017_4_30) {
upgradeFrom20170430to20170524(db);
version = VERSION_2017_5_24;
}
if (version == VERSION_2017_5_24) {
upgradeFrom20170525to20170728(db);
version = VERSION_2017_7_28;
}
if (version == VERSION_2017_7_28) {
upgradeFrom20170728to20170811(db);
version = VERSION_2017_8_11;
}
if (version != CUR_DATABASE_VERSION) {
LogUtil.E(TAG, "Upgrade unsuccessful -- destroying old data during upgrade");
db.execSQL("DROP TABLE IF EXISTS " + Tables.WALLPAPER);
db.execSQL("DROP TABLE IF EXISTS " + Tables.GALLERY);
onCreate(db);
version = CUR_DATABASE_VERSION;
}
}
private void upgradeFrom20161230to20170430(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + Tables.WALLPAPER
+ " ADD COLUMN " + Wallpaper.COLUMN_NAME_LIKED + " INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE " + Tables.WALLPAPER
+ " ADD COLUMN " + Wallpaper.COLUMN_NAME_CHECKSUM + " TEXT");
}
private void upgradeFrom20170430to20170524(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.GALLERY + " ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ StyleContract.GalleryWallpaper.COLUMN_NAME_CUSTOM_URI + " TEXT NOT NULL,"
+ StyleContract.GalleryWallpaper.COLUMN_NAME_IS_TREE_URI + " INTEGER,"
+ StyleContract.GalleryWallpaper.COLUMN_NAME_DATE_TIME + " INTEGER,"
+ StyleContract.GalleryWallpaper.COLUMN_NAME_LOCATION + " TEXT,"
+ StyleContract.GalleryWallpaper.COLUMN_NAME_HAS_METADATA + " INTEGER DEFAULT 0,"
+ "UNIQUE (" + StyleContract.GalleryWallpaper.COLUMN_NAME_CUSTOM_URI
+ ") ON CONFLICT REPLACE);");
}
private void upgradeFrom20170525to20170728(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.ADVANCE_WALLPAPER + " ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ AdvanceWallpaper.COLUMN_NAME_WALLPAPER_ID + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_ICON_URL + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_DOWNLOAD_URL + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_NAME + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_AUTHOR + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_STORE_PATH + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_LINK + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_PROVIDER_NAME + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_CHECKSUM + " TEXT,"
+ AdvanceWallpaper.COLUMN_NAME_SELECTED + " INTEGER DEFAULT 0);");
}
private void upgradeFrom20170728to20170811(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + Tables.ADVANCE_WALLPAPER
+ " ADD COLUMN " + AdvanceWallpaper.COLUMN_NAME_LAZY_DOWNLOAD
+ " INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE " + Tables.ADVANCE_WALLPAPER
+ " ADD COLUMN " + AdvanceWallpaper.COLUMN_NAME_NEED_AD
+ " INTEGER NOT NULL DEFAULT 0");
}
public static void deleteDatabase(Context context) {
context.deleteDatabase(DATABASE_NAME);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/provider/StyleProvider.java
================================================
package com.yalin.style.data.repository.datasource.provider;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract.AdvanceWallpaper;
import com.yalin.style.data.repository.datasource.provider.StyleContract.GalleryWallpaper;
import com.yalin.style.data.repository.datasource.provider.StyleContract.Wallpaper;
import com.yalin.style.data.utils.SelectionBuilder;
import com.yalin.style.data.utils.WallpaperFileHelper;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* YaLin 2016/12/30.
*/
public class StyleProvider extends ContentProvider {
private static final String TAG = "StyleProvider";
private StyleDatabase mOpenHelper;
private StyleProviderUriMatcher mUriMatcher;
@Override
public boolean onCreate() {
mOpenHelper = new StyleDatabase(getContext());
mUriMatcher = new StyleProviderUriMatcher();
return true;
}
private void deleteDatabase() {
mOpenHelper.close();
Context context = getContext();
StyleDatabase.deleteDatabase(context);
mOpenHelper = new StyleDatabase(context);
}
@NonNull
@Override
public ContentProviderResult[] applyBatch(@NonNull ArrayList operations)
throws OperationApplicationException {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
final int numOperations = operations.size();
final ContentProviderResult[] results = new ContentProviderResult[numOperations];
for (int i = 0; i < numOperations; i++) {
results[i] = operations.get(i).apply(this, results, i);
}
db.setTransactionSuccessful();
return results;
} finally {
db.endTransaction();
}
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs,
String sortOrder) {
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
StyleUriEnum uriEnum = mUriMatcher.matchUri(uri);
LogUtil.D(TAG, "uri=" + uri + " code=" + uriEnum.code + " proj=" +
Arrays.toString(projection) + " selection=" + selection + " args="
+ Arrays.toString(selectionArgs) + ")");
switch (uriEnum) {
case WALLPAPER:
case WALLPAPER_ID:
case WALLPAPER_LIKED:
case ADVANCE_WALLPAPER:
case ADVANCE_WALLPAPER_ID:
case ADVANCE_WALLPAPER_SELECTED: {
final SelectionBuilder builder = buildSimpleSelection(uri);
return builder.query(db, projection, BaseColumns._ID + " DESC");
}
case GALLERY:
case GALLERY_ID: {
final SelectionBuilder builder = buildSimpleSelection(uri);
return builder.query(db, projection, GalleryWallpaper._ID + " DESC");
}
default: {
final SelectionBuilder builder = buildExpandedSelection(uri, uriEnum.code);
return builder.query(db, projection, null);
}
}
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
LogUtil.D(TAG, "insert(uri=" + uri + ", values=" + values.toString()
+ ")");
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
StyleUriEnum uriEnum = mUriMatcher.matchUri(uri);
long id = db.insertOrThrow(uriEnum.table, null, values);
switch (uriEnum) {
case WALLPAPER:
return StyleContract.Wallpaper.buildWallpaperUri(
values.getAsString(StyleContract.Wallpaper.COLUMN_NAME_WALLPAPER_ID));
case GALLERY:
return StyleContract.GalleryWallpaper.buildGalleryWallpaperUri(id);
case ADVANCE_WALLPAPER: {
return AdvanceWallpaper.buildWallpaperUri(
values.getAsString(AdvanceWallpaper.COLUMN_NAME_WALLPAPER_ID));
}
default: {
throw new UnsupportedOperationException("Unknown insert uri: " + uri);
}
}
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
LogUtil.D(TAG, "delete(uri=" + uri + ")");
if (uri == StyleContract.BASE_CONTENT_URI) {
deleteDatabase();
return 1;
}
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
StyleUriEnum uriEnum = mUriMatcher.matchUri(uri);
final SelectionBuilder builder = buildSimpleSelection(uri);
switch (uriEnum) {
case WALLPAPER: {
builder.where(Wallpaper.COLUMN_NAME_LIKED + " = ?", "0");
break;
}
case ADVANCE_WALLPAPER: {
builder.where(AdvanceWallpaper.COLUMN_NAME_SELECTED + " = ?", "0");
break;
}
case GALLERY_URI: {
return builder.delete(db);
}
}
return builder.where(selection, selectionArgs).delete(db);
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
LogUtil.D(TAG, "update(uri=" + uri + ")");
if (uri == StyleContract.BASE_CONTENT_URI) {
deleteDatabase();
return 1;
}
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final SelectionBuilder builder = buildSimpleSelection(uri);
return builder.where(selection, selectionArgs).update(db, values);
}
@Nullable
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
LogUtil.D(TAG, "openReadFile(uri=" + uri + ",mode=" + mode + ")");
StyleUriEnum uriEnum = mUriMatcher.matchUri(uri);
switch (uriEnum) {
case WALLPAPER:
return WallpaperFileHelper.openReadFile(getContext(), queryUriForShow(), mode);
case WALLPAPER_ID:
return WallpaperFileHelper.openReadFile(getContext(), uri, mode);
case WALLPAPER_SAVE:
return WallpaperFileHelper.openWriteFile(getContext(), uri, mode);
default:
throw new FileNotFoundException("Cannot match uri : " + uri);
}
}
private Uri queryUriForShow() {
Cursor cursor = query(Wallpaper.CONTENT_URI,
new String[]{StyleContract.Wallpaper.COLUMN_NAME_IMAGE_URI},
null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
return Uri.parse(cursor.getString(0));
}
return null;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
private SelectionBuilder buildSimpleSelection(Uri uri) {
final SelectionBuilder builder = new SelectionBuilder();
StyleUriEnum uriEnum = mUriMatcher.matchUri(uri);
switch (uriEnum) {
case WALLPAPER:
case ADVANCE_WALLPAPER: {
return builder.table(uriEnum.table);
}
case WALLPAPER_ID: {
String wallpaperId = StyleContract.Wallpaper.getWallpaperId(uri);
return builder.table(StyleDatabase.Tables.WALLPAPER)
.where(Wallpaper.COLUMN_NAME_WALLPAPER_ID + " = ?", wallpaperId);
}
case WALLPAPER_LIKE: {
String wallpaperId = StyleContract.Wallpaper.getWallpaperLikeId(uri);
return builder.table(StyleDatabase.Tables.WALLPAPER)
.where(Wallpaper.COLUMN_NAME_WALLPAPER_ID + " = ?", wallpaperId);
}
case WALLPAPER_LIKED: {
return builder.table(StyleDatabase.Tables.WALLPAPER)
.where(Wallpaper.COLUMN_NAME_LIKED + " = ?", "1");
}
case GALLERY: {
return builder.table(StyleDatabase.Tables.GALLERY);
}
case GALLERY_ID: {
String galleryWallpaperId
= StyleContract.GalleryWallpaper.getGalleryWallpaperId(uri);
return builder.table(StyleDatabase.Tables.GALLERY)
.where(GalleryWallpaper._ID + " = ?", galleryWallpaperId);
}
case GALLERY_URI: {
String uriString
= StyleContract.GalleryWallpaper.getGalleryWallpaperDeleteUri(uri);
return builder.table(StyleDatabase.Tables.GALLERY)
.where(GalleryWallpaper.COLUMN_NAME_CUSTOM_URI + " = ?", uriString);
}
case ADVANCE_WALLPAPER_ID: {
String wallpaperId = AdvanceWallpaper.getWallpaperId(uri);
return builder.table(StyleDatabase.Tables.ADVANCE_WALLPAPER)
.where(AdvanceWallpaper.COLUMN_NAME_WALLPAPER_ID + " = ?", wallpaperId);
}
case ADVANCE_WALLPAPER_SELECTED: {
return builder.table(StyleDatabase.Tables.ADVANCE_WALLPAPER)
.where(AdvanceWallpaper.COLUMN_NAME_SELECTED + " = ?", String.valueOf(1));
}
default: {
throw new UnsupportedOperationException("Unknown uri for " + uri);
}
}
}
private SelectionBuilder buildExpandedSelection(Uri uri, int match) {
final SelectionBuilder builder = new SelectionBuilder();
StyleUriEnum uriEnum = mUriMatcher.matchUri(uri);
if (uriEnum == null) {
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
switch (uriEnum) {
default: {
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/provider/StyleProviderUriMatcher.java
================================================
package com.yalin.style.data.repository.datasource.provider;
import android.content.UriMatcher;
import android.net.Uri;
import android.util.SparseArray;
/**
* YaLin 2016/12/30.
*/
public class StyleProviderUriMatcher {
private UriMatcher mUriMatcher;
private SparseArray mEnumsMap = new SparseArray<>();
public StyleProviderUriMatcher() {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
buildUriMatcher();
}
private void buildUriMatcher() {
final String authority = StyleContract.AUTHORITY;
StyleUriEnum[] uris = StyleUriEnum.values();
for (StyleUriEnum uri : uris) {
mUriMatcher.addURI(authority, uri.path, uri.code);
}
buildEnumsMap();
}
private void buildEnumsMap() {
StyleUriEnum[] uris = StyleUriEnum.values();
for (StyleUriEnum uri : uris) {
mEnumsMap.put(uri.code, uri);
}
}
public StyleUriEnum matchUri(Uri uri) {
final int code = mUriMatcher.match(uri);
try {
return matchCode(code);
} catch (UnsupportedOperationException e) {
throw new UnsupportedOperationException("Unknown uri " + uri);
}
}
public StyleUriEnum matchCode(int code) {
StyleUriEnum uriEnum = mEnumsMap.get(code);
if (uriEnum != null) {
return uriEnum;
} else {
throw new UnsupportedOperationException("Unknown uri with code " + code);
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/provider/StyleUriEnum.java
================================================
package com.yalin.style.data.repository.datasource.provider;
/**
* YaLin 2016/12/30.
*/
public enum StyleUriEnum {
WALLPAPER(100, "wallpaper", StyleDatabase.Tables.WALLPAPER),
WALLPAPER_SAVE(103, "wallpaper/save/*", null),
WALLPAPER_LIKE(104, "wallpaper/like/*", null),
WALLPAPER_LIKED(105, "wallpaper/liked", null),
WALLPAPER_ID(102, "wallpaper/*", null),
GALLERY(200, "gallery", StyleDatabase.Tables.GALLERY),
GALLERY_URI(202, "gallery/uri/*", null),
GALLERY_ID(201, "gallery/*", null),
ADVANCE_WALLPAPER(300, "advance_wallpaper", StyleDatabase.Tables.ADVANCE_WALLPAPER),
ADVANCE_WALLPAPER_SELECTED(302, "advance_wallpaper/selected", null),
ADVANCE_WALLPAPER_ID(301, "advance_wallpaper/*", null);
public int code;
public String path;
public String table;
StyleUriEnum(int code, String path, String table) {
this.code = code;
this.path = path;
this.table = table;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/RemoteStyleDataFetcher.java
================================================
package com.yalin.style.data.repository.datasource.sync;
import android.content.Context;
import com.yalin.style.data.BuildConfig;
import com.yalin.style.data.repository.datasource.net.DataFetcher;
/**
* YaLin 2017/1/3.
*/
public class RemoteStyleDataFetcher extends DataFetcher {
public RemoteStyleDataFetcher(Context context) {
super(context);
}
@Override
protected String getUrl() {
return BuildConfig.SERVER_WALLPAPER_ENDPOINT;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/StyleDataHandler.java
================================================
package com.yalin.style.data.repository.datasource.sync;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.RemoteException;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.io.AdvanceWallpaperHandler;
import com.yalin.style.data.repository.datasource.io.JSONHandler;
import com.yalin.style.data.repository.datasource.io.WallpapersHandler;
import com.yalin.style.data.repository.datasource.provider.StyleContract;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
/**
* YaLin 2017/1/3.
*/
public class StyleDataHandler {
private static final String TAG = "StyleDataHandler";
private static final String DATA_KEY_WALLPAPER = "wallpapers";
private static final String DATA_KEY_ADVANCE_WALLPAPER = "advance_wallpapers";
private static final String[] DATA_KEYS_IN_ORDER = {
DATA_KEY_WALLPAPER,
DATA_KEY_ADVANCE_WALLPAPER
};
Context mContext = null;
WallpapersHandler mWallpapersHandler = null;
AdvanceWallpaperHandler mAdvanceWallpapersHandler = null;
HashMap mHandlerForKey = new HashMap<>();
private int mContentProviderOperationsDone = 0;
public StyleDataHandler(Context context) {
mContext = context;
}
public void applyStyleData(String[] dataBodies) throws IOException {
LogUtil.D(TAG, "Applying data from " + dataBodies.length + " files");
mHandlerForKey.put(DATA_KEY_WALLPAPER, mWallpapersHandler = new WallpapersHandler(mContext));
mHandlerForKey.put(DATA_KEY_ADVANCE_WALLPAPER,
mAdvanceWallpapersHandler = new AdvanceWallpaperHandler(mContext));
LogUtil.D(TAG, "Processing " + dataBodies.length + " JSON objects.");
for (int i = 0; i < dataBodies.length; i++) {
LogUtil.D(TAG, "Processing json object #" + (i + 1) + " of " + dataBodies.length);
processDataBody(dataBodies[i]);
}
ArrayList batch = new ArrayList<>();
for (String key : DATA_KEYS_IN_ORDER) {
LogUtil.D(TAG, "Building content provider operations for: " + key);
mHandlerForKey.get(key).makeContentProviderOperations(batch);
LogUtil.D(TAG, "Content provider operations so far: " + batch.size());
}
LogUtil.D(TAG, "Applying " + batch.size() + " content provider operations.");
try {
int operations = batch.size();
if (operations > 0) {
mContext.getContentResolver().applyBatch(StyleContract.AUTHORITY, batch);
}
LogUtil.D(TAG, "Successfully applied " + operations + " content provider operations.");
mContentProviderOperationsDone += operations;
} catch (RemoteException ex) {
LogUtil.D(TAG, "RemoteException while applying content provider operations.");
throw new RuntimeException("Error executing content provider batch operation", ex);
} catch (OperationApplicationException ex) {
LogUtil.D(TAG, "OperationApplicationException while applying content provider operations.");
throw new RuntimeException("Error executing content provider batch operation", ex);
}
LogUtil.D(TAG, "Notifying changes on all top-level paths on Content Resolver.");
ContentResolver resolver = mContext.getContentResolver();
for (String path : StyleContract.TOP_LEVEL_PATHS) {
Uri uri = StyleContract.BASE_CONTENT_URI.buildUpon().appendPath(path).build();
resolver.notifyChange(uri, null);
}
LogUtil.D(TAG, "Done applying conference data.");
}
private void processDataBody(String dataBody) throws IOException {
JsonParser parser = new JsonParser();
try (JsonReader reader = new JsonReader(new StringReader(dataBody))) {
reader.setLenient(true); // To err is human
// the whole file is a single JSON object
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
if (mHandlerForKey.containsKey(key)) {
LogUtil.D(TAG, "Processing key in conference data json: " + key);
mHandlerForKey.get(key).process(parser.parse(reader));
} else {
LogUtil.D(TAG, "Skipping unknown key in conference data json: " + key);
reader.skipValue();
}
}
reader.endObject();
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/SyncAdapter.java
================================================
package com.yalin.style.data.repository.datasource.sync;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import com.yalin.style.data.log.LogUtil;
/**
* YaLin 2017/1/3.
*/
public class SyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = "SyncAdapter";
public static final String SYNC_MANUALLY = "syn_manually";
private final Context mContext;
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
mContext = context;
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) ->
LogUtil.F(TAG, "Uncaught sync exception, suppressing UI in release build.",
throwable));
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
LogUtil.F(TAG, "PerformSync.");
if (extras.getBoolean(SYNC_MANUALLY)) {
LogUtil.D(TAG, "Manually sync.");
}
new SyncHelper(mContext).performSync(syncResult, extras);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/SyncHelper.java
================================================
package com.yalin.style.data.repository.datasource.sync;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncResult;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.text.TextUtils;
import com.yalin.style.data.BuildConfig;
import com.yalin.style.data.SyncConfig;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract;
import java.io.IOException;
/**
* YaLin 2017/1/3.
*/
public class SyncHelper {
private static final String TAG = "SyncHelper";
private final Context mContext;
private StyleDataHandler mDataHandler;
public SyncHelper(Context context) {
mContext = context;
mDataHandler = new StyleDataHandler(mContext);
}
public boolean performSync(SyncResult syncResult, Bundle extras) {
try {
doStyleSync();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private boolean doStyleSync() throws IOException {
if (!isOnline()) {
LogUtil.D(TAG, "Not attempting remote sync because device is OFFLINE");
return false;
}
LogUtil.D(TAG, "Starting remote sync.");
String data = new RemoteStyleDataFetcher(mContext).fetchDataIfNewer();
if (!TextUtils.isEmpty(data)) {
mDataHandler.applyStyleData(new String[]{data});
}
return true;
}
private boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) mContext
.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null &&
cm.getActiveNetworkInfo().isConnectedOrConnecting();
}
public static void updateSyncInterval(final Context context) {
Account account = com.yalin.style.data.repository.datasource.sync.account.Account.getAccount();
LogUtil.D(TAG, "Checking sync interval");
long recommended = calculateRecommendedSyncInterval(context);
LogUtil.D(TAG, "Setting up sync for account, interval " + recommended + "ms");
ContentResolver.setIsSyncable(account, StyleContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, StyleContract.AUTHORITY, true);
ContentResolver
.addPeriodicSync(account, StyleContract.AUTHORITY, new Bundle(), recommended / 1000L);
}
private static long calculateRecommendedSyncInterval(final Context context) {
if (BuildConfig.DEMO_MODE) {
return SyncConfig.DEBUG_AUTO_SYNC_INTERVAL_LONG;
} else {
return SyncConfig.AUTO_SYNC_INTERVAL_LONG;
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/SyncService.java
================================================
package com.yalin.style.data.repository.datasource.sync;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
/**
* YaLin 2017/1/3.
*/
public class SyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static SyncAdapter sSyncAdapter = null;
@Override
public void onCreate() {
super.onCreate();
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), false);
}
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/account/Account.java
================================================
package com.yalin.style.data.repository.datasource.sync.account;
import android.accounts.AccountManager;
import android.content.Context;
import com.yalin.style.data.log.LogUtil;
/**
* YaLin 2017/1/3.
*/
public class Account {
public static final String ACCOUNT_TYPE = "com.yalin.style";
public static final String ACCOUNT_NAME = "Sync Account";
private static final String TAG = "Account";
private static android.accounts.Account mAccount;
public static android.accounts.Account createSyncAccount(Context context) {
AccountManager accountManager = (AccountManager) context
.getSystemService(Context.ACCOUNT_SERVICE);
android.accounts.Account account = getAccount();
if (accountManager.addAccountExplicitly(account, null, null)) {
return account;
} else {
LogUtil.D(TAG, "Unable to create account");
return null;
}
}
public static android.accounts.Account getAccount() {
if (mAccount == null) {
mAccount = new android.accounts.Account(ACCOUNT_NAME, ACCOUNT_TYPE);
}
return mAccount;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/account/Authenticator.java
================================================
package com.yalin.style.data.repository.datasource.sync.account;
import android.accounts.*;
import android.accounts.Account;
import android.content.Context;
import android.os.Bundle;
/**
* YaLin 2017/1/3.
*/
public class Authenticator extends AbstractAccountAuthenticator {
public Authenticator(Context context) {
super(context);
}
@Override
public Bundle editProperties(
AccountAuthenticatorResponse response, String accountType) {
throw new UnsupportedOperationException();
}
// Don't add additional accounts
@Override
public Bundle addAccount(
AccountAuthenticatorResponse response,
String accountType,
String authTokenType,
String[] requiredFeatures,
Bundle options) throws NetworkErrorException {
return null;
}
// Ignore attempts to confirm credentials
@Override
public Bundle confirmCredentials(
AccountAuthenticatorResponse response,
Account account,
Bundle options) throws NetworkErrorException {
return null;
}
// Getting an authentication token is not supported
@Override
public Bundle getAuthToken(
AccountAuthenticatorResponse response,
Account account,
String authTokenType,
Bundle options) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
// Getting a label for the auth token is not supported
@Override
public String getAuthTokenLabel(String authTokenType) {
throw new UnsupportedOperationException();
}
// Updating user credentials is not supported
@Override
public Bundle updateCredentials(
AccountAuthenticatorResponse response,
Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
// Checking features for the account is not supported
@Override
public Bundle hasFeatures(
AccountAuthenticatorResponse response,
Account account, String[] options) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/account/AuthenticatorService.java
================================================
package com.yalin.style.data.repository.datasource.sync.account;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
/**
* YaLin 2017/1/3.
*/
public class AuthenticatorService extends Service {
private Authenticator mAuthenticator;
@Override
public void onCreate() {
super.onCreate();
mAuthenticator = new Authenticator(this);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/repository/datasource/sync/gallery/GalleryScheduleService.kt
================================================
package com.yalin.style.data.repository.datasource.sync.gallery
import android.app.AlarmManager
import android.app.IntentService
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.database.Cursor
import com.yalin.style.data.entity.GalleryWallpaperEntity
import com.yalin.style.data.extensions.DelegateExt
import com.yalin.style.data.log.LogUtil
import com.yalin.style.data.repository.datasource.provider.StyleContract
import com.yalin.style.data.utils.notifyChange
import java.util.*
/**
* @author jinyalin
* @since 2017/6/9.
*/
class GalleryScheduleService : IntentService(TAG) {
companion object {
val TAG = "GalleryScheduleService"
val PREF_ROTATE_INTERVAL_MIN = "rotate_interval_min"
val PREF_CURRENT_SHOW_WALLPAPER_ID = "current_gallery_wallpaper_id"
val DEFAULT_ROTATE_INTERVAL_MIN = 60 * 6
val ACTION_START_UP = "com.yalin.style.ACTION_START_UP"
val ACTION_SHUT_DOWN = "com.yalin.style.ACTION_SHUT_DOWN"
val ACTION_SCHEDULE = "com.yalin.style.ACTION_SCHEDULE"
val ACTION_SET_INTERVAL = "com.yalin.style.ACTION_SET_INTERVAL"
val INTERVAL_KEY = "interval"
fun startUp(context: Context) {
val intent = Intent(ACTION_START_UP).setComponent(ComponentName(context,
GalleryScheduleService::class.java))
context.startService(intent)
}
fun shutDown(context: Context) {
val intent = Intent(ACTION_SHUT_DOWN).setComponent(ComponentName(context,
GalleryScheduleService::class.java))
context.startService(intent)
}
fun publish(context: Context) {
val intent = Intent(ACTION_SCHEDULE).setComponent(ComponentName(context,
GalleryScheduleService::class.java))
context.startService(intent)
}
fun setInterval(context: Context, intervalMin: Int) {
val intent = Intent(ACTION_SET_INTERVAL).setComponent(ComponentName(context,
GalleryScheduleService::class.java))
intent.putExtra(INTERVAL_KEY, intervalMin)
context.startService(intent)
}
}
var rotateIntervalMin: Int by DelegateExt.preferences(this,
PREF_ROTATE_INTERVAL_MIN, DEFAULT_ROTATE_INTERVAL_MIN)
var currentShowWallpaperId: Long by DelegateExt.preferences(this,
PREF_CURRENT_SHOW_WALLPAPER_ID, -1)
override fun onCreate() {
super.onCreate()
}
override fun onHandleIntent(intent: Intent?) {
if (intent != null) {
handleCommand(intent, intent.action)
}
}
override fun onDestroy() {
super.onDestroy()
}
private fun handleCommand(intent: Intent, action: String) {
when (action) {
ACTION_START_UP -> startUp()
ACTION_SCHEDULE -> scheduleNext()
ACTION_SHUT_DOWN -> shutDown()
ACTION_SET_INTERVAL -> setInterval(intent.getIntExtra(INTERVAL_KEY,
DEFAULT_ROTATE_INTERVAL_MIN))
}
}
private fun startUp() {
LogUtil.D(TAG, "Start up gallery schedule service.")
setNextAlarm()
}
private fun scheduleNext() {
LogUtil.D(TAG, "Schedule next gallery wallpaper.")
setNextAlarm()
publicNextWallpaper()
}
private fun shutDown() {
LogUtil.D(TAG, "Shut down gallery schedule service.")
cancelAlarm()
stopSelf()
}
private fun setInterval(intervalMin: Int) {
rotateIntervalMin = intervalMin
LogUtil.D(TAG, "Set schedule interval $rotateIntervalMin = $intervalMin")
setNextAlarm()
}
private fun setNextAlarm() {
if (rotateIntervalMin > 0) {
setUpdateAlarm(System.currentTimeMillis() + rotateIntervalMin * 60 * 1000)
} else {
cancelAlarm()
}
}
private fun setUpdateAlarm(nextTimeMillis: Long) {
if (nextTimeMillis < System.currentTimeMillis()) {
LogUtil.D(TAG, "Refusing to schedule next artwork in the past")
return
}
val am = getSystemService(Context.ALARM_SERVICE) as AlarmManager
am.set(AlarmManager.RTC, nextTimeMillis, getHandleNextCommandPendingIntent(this))
LogUtil.D(TAG, "Scheduling next gallery at " + Date(nextTimeMillis))
}
private fun cancelAlarm() {
LogUtil.D(TAG, "Cancel schedule alarm")
val am = getSystemService(Context.ALARM_SERVICE) as AlarmManager
am.cancel(getHandleNextCommandPendingIntent(this))
}
private fun getHandleNextCommandPendingIntent(context: Context): PendingIntent {
return PendingIntent.getService(context, 0,
Intent(ACTION_SCHEDULE).setComponent(ComponentName(context, javaClass)),
PendingIntent.FLAG_UPDATE_CURRENT)
}
private fun publicNextWallpaper() {
var cursor: Cursor? = null
val validWallpapers = ArrayList()
try {
cursor = contentResolver.query(StyleContract.GalleryWallpaper.CONTENT_URI,
null, null, null, null)
validWallpapers.addAll(GalleryWallpaperEntity.readCursor(this, cursor))
} finally {
if (cursor != null) {
cursor.close()
}
}
if (validWallpapers.size > 1) {
val random = Random()
while (true) {
val selectedEntity = validWallpapers[random.nextInt(validWallpapers.size)]
if (selectedEntity.id != currentShowWallpaperId) {
currentShowWallpaperId = selectedEntity.id
break
}
}
} else if (validWallpapers.size == 1) {
if (currentShowWallpaperId != validWallpapers[0].id) {
currentShowWallpaperId = validWallpapers[0].id
}
} else {
if (currentShowWallpaperId != -1L) {
currentShowWallpaperId = -1
}
}
LogUtil.D(TAG, "Current select wallpaper wallpaperId : $currentShowWallpaperId ")
notifyChange(this, StyleContract.GalleryWallpaper.CONTENT_URI)
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/ChecksumUtil.java
================================================
package com.yalin.style.data.utils;
import android.util.Base64;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author jinyalin
* @since 2017/4/25.
*/
public class ChecksumUtil {
public static String getChecksum(File file) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
try (InputStream is = new FileInputStream(file);
DigestInputStream dis = new DigestInputStream(is, md)) {
byte[] buffer = new byte[2048];
//noinspection StatementWithEmptyBody
while (dis.read(buffer) > 0) {
}
byte[] digest = dis.getMessageDigest().digest();
return Base64.encodeToString(digest, Base64.URL_SAFE).trim();
} catch (Exception e) {
return null;
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/DeviceUtil.java
================================================
package com.yalin.style.data.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.provider.Settings;
/**
* @author jinyalin
* @since 2017/4/25.
*/
public class DeviceUtil {
private DeviceUtil() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
public static int getSDKVersion() {
return android.os.Build.VERSION.SDK_INT;
}
@SuppressLint("HardwareIds")
public static String getAndroidID(Context context) {
return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
}
public static String getManufacturer() {
return Build.MANUFACTURER;
}
public static String getModel() {
String model = Build.MODEL;
if (model != null) {
model = model.trim().replaceAll("\\s*", "");
} else {
model = "";
}
return model;
}
public static String getBrand() {
String brand = Build.BRAND;
if (brand != null) {
brand = brand.trim().replaceAll("\\s*", "");
} else {
brand = "";
}
return brand;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/FacetIdUtil.java
================================================
package com.yalin.style.data.utils;
import android.content.Context;
/**
* @author jinyalin
* @since 2017/4/26.
*/
public class FacetIdUtil {
static {
System.loadLibrary("facet_id-lib");
}
public static boolean checkCurrentFacetId(Context context) {
return checkCurrentFacetId(context, getUid(context));
}
public static String getFacetId(Context context) {
return getFacetId(context, getUid(context));
}
private static native boolean checkCurrentFacetId(Context context, int uId);
private static native String getFacetId(Context context, int uId);
private static int getUid(Context context) {
if (context != null) {
return context.getApplicationInfo().uid;
} else {
return -1;
}
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/HttpRequestUtil.java
================================================
package com.yalin.style.data.utils;
import android.content.Context;
import com.google.gson.Gson;
import com.yalin.style.data.entity.DeviceInfo;
import com.yalin.style.data.entity.HttpRequestBody;
/**
* @author jinyalin
* @since 2017/4/25.
*/
public class HttpRequestUtil {
private static Gson gson = new Gson();
public static String getRequestBody(Context context) {
HttpRequestBody requestBody = new HttpRequestBody(context, getDeviceJson(context));
return gson.toJson(requestBody);
}
private static DeviceInfo getDeviceJson(Context context) {
int sdkVersion = DeviceUtil.getSDKVersion();
String androidId = DeviceUtil.getAndroidID(context);
String manufacturer = DeviceUtil.getManufacturer();
String brand = DeviceUtil.getBrand();
String model = DeviceUtil.getModel();
return new DeviceInfo(sdkVersion, androidId, manufacturer, brand, model);
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/NativeFileHelper.kt
================================================
package com.yalin.style.data.utils
import android.content.Context
import android.text.TextUtils
import com.yalin.style.data.log.LogUtil
import java.io.File
/**
* @author jinyalin
* @since 2017/8/10.
*/
private val TAG = "NativeFileHelper"
private var nativePath: String? = null
fun getNativeDir(context: Context): File {
if (TextUtils.isEmpty(nativePath)) {
val cacheDir = context.cacheDir
val nativeDir = File(cacheDir.parent, "lib")
nativeDir.mkdirs()
nativePath = nativeDir.absolutePath
}
return File(nativePath)
}
fun getNativeFileName(componentPath: String, libName: String): String {
return "plugin_" + getComponentName(componentPath) + "_" + libName + ".so"
}
fun clearNativeFiles(context: Context, componentPath: String) {
val files = getNativeFiles(context, componentPath)
for (file in files) {
file.delete()
}
}
private fun getNativeFiles(context: Context, componentPath: String): Array {
val nativePrefix = "plugin_" + getComponentName(componentPath)
return getNativeDir(context).listFiles({ file -> file.name.contains(nativePrefix) })
}
private fun getComponentName(componentPath: String): String {
var componentName: String
try {
val tmp = componentPath.split("/".toRegex())
componentName = tmp[tmp.size - 1].split("\\.".toRegex())[0]
} catch (e: Exception) {
componentName = componentPath.hashCode().toString()
}
LogUtil.D(TAG, "getComponentName for $componentPath result :$componentName")
return componentName
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/NetworkUtil.java
================================================
package com.yalin.style.data.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
/**
* @author jinyalin
* @since 2017/4/29.
*/
public class NetworkUtil {
public static boolean isThereInternetConnection(Context context) {
boolean isConnected;
ConnectivityManager connectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting());
return isConnected;
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/SelectionBuilder.java
================================================
package com.yalin.style.data.utils;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import com.yalin.style.data.log.LogUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* YaLin 2016/12/30.
*/
public class SelectionBuilder {
private static final String TAG = "SelectionBuilder";
private String mTable = null;
private Map mProjectionMap = new HashMap<>();
private StringBuilder mSelection = new StringBuilder();
private ArrayList mSelectionArgs = new ArrayList<>();
private String mGroupBy = null;
private String mHaving = null;
/**
* Reset any internal state, allowing this builder to be recycled.
*/
public SelectionBuilder reset() {
mTable = null;
mGroupBy = null;
mHaving = null;
mSelection.setLength(0);
mSelectionArgs.clear();
return this;
}
/**
* Append the given selection clause to the internal state. Each clause is surrounded with
* parenthesis and combined using {@code AND}.
*/
public SelectionBuilder where(String selection, String... selectionArgs) {
if (TextUtils.isEmpty(selection)) {
if (selectionArgs != null && selectionArgs.length > 0) {
throw new IllegalArgumentException(
"Valid selection required when including arguments=");
}
// Shortcut when clause is empty
return this;
}
if (mSelection.length() > 0) {
mSelection.append(" AND ");
}
mSelection.append("(").append(selection).append(")");
if (selectionArgs != null) {
Collections.addAll(mSelectionArgs, selectionArgs);
}
return this;
}
public SelectionBuilder groupBy(String groupBy) {
mGroupBy = groupBy;
return this;
}
public SelectionBuilder having(String having) {
mHaving = having;
return this;
}
public SelectionBuilder table(String table) {
mTable = table;
return this;
}
/**
* Replace positional params in table. Use for JOIN ON conditions.
*/
public SelectionBuilder table(String table, String... tableParams) {
if (tableParams != null && tableParams.length > 0) {
String[] parts = table.split("[?]", tableParams.length + 1);
StringBuilder sb = new StringBuilder(parts[0]);
for (int i = 1; i < parts.length; i++) {
sb.append('"').append(tableParams[i - 1]).append('"')
.append(parts[i]);
}
mTable = sb.toString();
} else {
mTable = table;
}
return this;
}
private void assertTable() {
if (mTable == null) {
throw new IllegalStateException("Table not specified");
}
}
public SelectionBuilder mapToTable(String column, String table) {
mProjectionMap.put(column, table + "." + column);
return this;
}
public SelectionBuilder map(String fromColumn, String toClause) {
mProjectionMap.put(fromColumn, toClause + " AS " + fromColumn);
return this;
}
/**
* Return selection string for current internal state.
*
* @see #getSelectionArgs()
*/
public String getSelection() {
return mSelection.toString();
}
/**
* Return selection arguments for current internal state.
*
* @see #getSelection()
*/
public String[] getSelectionArgs() {
return mSelectionArgs.toArray(new String[mSelectionArgs.size()]);
}
private void mapColumns(String[] columns) {
for (int i = 0; i < columns.length; i++) {
final String target = mProjectionMap.get(columns[i]);
if (target != null) {
columns[i] = target;
}
}
}
@Override
public String toString() {
return "SelectionBuilder[table=" + mTable + ", selection=" + getSelection()
+ ", selectionArgs=" + Arrays.toString(getSelectionArgs())
+ "projectionMap = " + mProjectionMap + " ]";
}
/**
* Execute query using the current internal state as {@code WHERE} clause.
*/
public Cursor query(SQLiteDatabase db, String[] columns, String orderBy) {
return query(db, false, columns, orderBy, null);
}
/**
* Execute query using the current internal state as {@code WHERE} clause.
*/
public Cursor query(SQLiteDatabase db, boolean distinct, String[] columns, String orderBy,
String limit) {
assertTable();
if (columns != null) {
mapColumns(columns);
}
LogUtil.D(TAG, "query(columns=" + Arrays.toString(columns)
+ ", distinct=" + distinct + ") " + this);
return db.query(distinct, mTable, columns, getSelection(), getSelectionArgs(), mGroupBy,
mHaving, orderBy, limit);
}
/**
* Execute update using the current internal state as {@code WHERE} clause.
*/
public int update(SQLiteDatabase db, ContentValues values) {
assertTable();
LogUtil.D(TAG, "update() " + this);
return db.update(mTable, values, getSelection(), getSelectionArgs());
}
/**
* Execute delete using the current internal state as {@code WHERE} clause.
*/
public int delete(SQLiteDatabase db) {
assertTable();
LogUtil.D(TAG, "delete() " + this);
return db.delete(mTable, getSelection(), getSelectionArgs());
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/TimeUtil.java
================================================
package com.yalin.style.data.utils;
import android.content.Context;
/**
* YaLin 2017/1/3.
*/
public class TimeUtil {
public static long getCurrentTime(final Context context) {
return System.currentTimeMillis();
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/UriUtil.kt
================================================
package com.yalin.style.data.utils
import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.database.Cursor
import android.database.SQLException
import android.net.Uri
import android.os.Binder
import android.os.Build
import android.provider.DocumentsContract
import android.text.TextUtils
import com.yalin.style.data.log.LogUtil
import com.yalin.style.domain.GalleryWallpaper
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
/**
* @author jinyalin
* @since 2017/5/25.
*/
private val TAG = "UriUtil"
fun isTreeUri(possibleTreeUri: Uri): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return DocumentsContract.isTreeUri(possibleTreeUri)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Prior to N we can't directly check if the URI is a tree URI, so we have to just try it
try {
val treeDocumentId = DocumentsContract.getTreeDocumentId(possibleTreeUri)
return !TextUtils.isEmpty(treeDocumentId)
} catch (e: IllegalArgumentException) {
// Definitely not a tree URI
return false
}
}
// No tree URIs prior to Lollipop
return false
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
fun getImagesFromTreeUri(context: Context, treeUri: Uri, maxImages: Int): List {
val images = ArrayList()
val directories = LinkedList()
directories.add(DocumentsContract.getTreeDocumentId(treeUri))
while (images.size < maxImages && !directories.isEmpty()) {
val parentDocumentId = directories.poll()
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri,
parentDocumentId)
var children: Cursor?
try {
children = context.contentResolver.query(childrenUri,
arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_MIME_TYPE), null, null, null)
} catch (e: SecurityException) {
// No longer can read this URI, which means no images from this URI
// This a temporary state as the next onLoadFinished() will remove this item entirely
children = null
}
if (children == null) {
continue
}
while (children.moveToNext()) {
val documentId = children.getString(
children.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID))
val mimeType = children.getString(
children.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE))
if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) {
directories.add(documentId)
} else if (mimeType != null && mimeType.startsWith("image/")) {
// Add images to the list
images.add(DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId))
}
if (images.size == maxImages) {
break
}
}
children.close()
}
return images
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
fun getDisplayNameForTreeUri(context: Context, treeUri: Uri): String? {
val documentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri,
DocumentsContract.getTreeDocumentId(treeUri))
var data: Cursor? = null
try {
data = context.contentResolver.query(documentUri,
arrayOf(DocumentsContract.Document.COLUMN_DISPLAY_NAME), null, null, null)
} catch (e: Throwable) {
LogUtil.E(TAG, "getDisplayNameForTreeUri failed.", e)
}
var displayName: String? = null
if (data != null && data.moveToNext()) {
displayName = data.getString(
data.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME))
}
data?.close()
return displayName
}
fun processUriPermission(context: Context, galleryWallpaper: GalleryWallpaper) {
val uri = Uri.parse(galleryWallpaper.uri)
if (galleryWallpaper.isTreeUri) {
try {
context.contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION)
} catch (e: SecurityException) {
// You can't persist URI permissions from your own app, so this fails.
// We'll still have access to it directly
LogUtil.E(TAG, "processUriPermission exception ", e)
}
} else {
val haveUriPermission = context.checkUriPermission(uri,
Binder.getCallingPid(), Binder.getCallingUid(),
Intent.FLAG_GRANT_READ_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED
// If we only have permission to this URI via URI permissions (rather than directly,
// such as if the URI is from our own app), it is from an external source and we need
// to make sure to gain persistent access to the URI's content
if (haveUriPermission) {
var persistedPermission = false
// Try to persist access to the URI, saving us from having to store a local copy
if (DocumentsContract.isDocumentUri(context, uri)) {
try {
context.contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION)
persistedPermission = true
// If we have a persisted URI permission, we don't need a local copy
val cachedFile = getCacheFileForUri(context, galleryWallpaper.uri)
if (cachedFile != null && cachedFile.exists()) {
if (!cachedFile.delete()) {
LogUtil.D(TAG, "Unable to delete " + cachedFile)
}
}
} catch (e: SecurityException) {
// If we don't have FLAG_GRANT_PERSISTABLE_URI_PERMISSION (such as when using ACTION_GET_CONTENT),
// this will fail. We'll need to make a local copy (handled below)
LogUtil.E(TAG, "processUriPermission exception ", e)
}
}
if (!persistedPermission) {
// We only need to make a local copy if we weren't able to persist the permission
try {
writeUriToFile(context, galleryWallpaper.uri,
getCacheFileForUri(context, galleryWallpaper.uri))
} catch (e: IOException) {
LogUtil.E(TAG, "Error downloading gallery image "
+ galleryWallpaper.uri, e)
throw SQLException("Error downloading gallery image "
+ galleryWallpaper.uri, e)
}
}
} else {
// On API 25 and lower, we don't get URI permissions to URIs
// from our own package so we manage those URI permissions manually
val resolver = context.contentResolver
try {
resolver.call(uri, "takePersistableUriPermission",
uri.toString(), null)
} catch (e: Exception) {
LogUtil.E(TAG, "Unable to manually persist uri permissions to " + uri, e)
}
}
}
}
private fun writeUriToFile(context: Context?, uri: String, destFile: File?) {
if (context == null) {
return
}
if (destFile == null) {
throw IOException("Invalid destination for " + uri)
}
try {
val input = context.contentResolver.openInputStream(Uri.parse(uri)) ?: return
val fileOutput = FileOutputStream(destFile)
val buffer = ByteArray(1024)
var bytesRead = input.read(buffer)
while (bytesRead > 0) {
fileOutput.write(buffer, 0, bytesRead)
bytesRead = input.read(buffer)
}
fileOutput.flush()
} catch (e: SecurityException) {
throw IOException("Unable to read Uri: " + uri, e)
}
}
fun getCacheFileForUri(context: Context, imageUri: String): File? {
val directory = File(context.getExternalFilesDir(null), "gallery_images")
if (!directory.exists() && !directory.mkdirs()) {
return null
}
// Create a unique filename based on the imageUri
val uri = Uri.parse(imageUri)
val filename = StringBuilder()
filename.append(uri.scheme).append("_")
.append(uri.host).append("_")
var encodedPath = uri.encodedPath
if (!TextUtils.isEmpty(encodedPath)) {
val length = encodedPath.length
if (length > 60) {
encodedPath = encodedPath.substring(length - 60)
}
encodedPath = encodedPath.replace('/', '_')
filename.append(encodedPath).append("_")
}
try {
val md = MessageDigest.getInstance("MD5")
md.update(uri.toString().toByteArray(charset("UTF-8")))
val digest = md.digest()
for (b in digest) {
if (0xff and b.toInt() < 0x10) {
filename.append("0").append(Integer.toHexString(0xFF and b.toInt()))
} else {
filename.append(Integer.toHexString(0xFF and b.toInt()))
}
}
} catch (e: NoSuchAlgorithmException) {
filename.append(uri.toString().hashCode())
} catch (e: UnsupportedEncodingException) {
filename.append(uri.toString().hashCode())
}
return File(directory, filename.toString())
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/WallpaperFileHelper.java
================================================
package com.yalin.style.data.utils;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import com.yalin.style.data.log.LogUtil;
import com.yalin.style.data.repository.datasource.provider.StyleContract.Wallpaper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
/**
* YaLin On 2017/1/2.
*/
public class WallpaperFileHelper {
private static final String TAG = "WallpaperFileHelper";
public static final String WALLPAPER_FOLDER = "wallpaper";
public static final String ADVANCE_WALLPAPER_FOLDER = "component";
public static ParcelFileDescriptor openReadFile(Context context, Uri uri, String mode)
throws FileNotFoundException {
LogUtil.D(TAG, "Read file Uri=" + (uri == null ? null : uri.toString()));
String wallpaperId = Wallpaper.getWallpaperId(uri);
File directory = new File(context.getFilesDir(), WALLPAPER_FOLDER);
if (!directory.exists()) {
throw new FileNotFoundException("Wallpaper file : "
+ directory.toString() + " cannot found.");
}
File file = new File(directory, generateFileName(wallpaperId));
return ParcelFileDescriptor.open(file, parseMode(mode));
}
public static ParcelFileDescriptor openWriteFile(Context context, Uri uri, String mode)
throws FileNotFoundException {
String wallpaperId = Wallpaper.getWallpaperSaveId(uri);
File directory = new File(context.getFilesDir(), WALLPAPER_FOLDER);
if (!directory.exists() && !directory.mkdir()) {
throw new FileNotFoundException("Wallpaper save dir : "
+ directory.toString() + " cannot be create.");
}
File file = new File(directory, generateFileName(wallpaperId));
return ParcelFileDescriptor.open(file, parseMode(mode));
}
private static String generateFileName(String wallpaperId) {
return wallpaperId;
}
private static int parseMode(String mode) {
final int modeBits;
if ("r".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
} else if ("w".equals(mode) || "wt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else if ("wa".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_APPEND;
} else if ("rw".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE;
} else if ("rwt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else {
throw new IllegalArgumentException("Bad mode '" + mode + "'");
}
return modeBits;
}
public static boolean copyAssets(Context context, String name, File output) {
InputStream is = null;
FileOutputStream fos = null;
try {
is = context.getAssets().open(name);
fos = new FileOutputStream(output);
byte[] buffer = new byte[2048];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.flush();
return true;
} catch (IOException e) {
return false;
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void deleteOldWallpapers(Context context, Set excludeIds) {
File directory = new File(context.getFilesDir(), WALLPAPER_FOLDER);
if (!directory.exists()) {
return;
}
Set namesSet = new HashSet<>();
for (String wallpaperId : excludeIds) {
namesSet.add(generateFileName(wallpaperId));
}
File[] files = directory.listFiles(fileName ->
!namesSet.contains(fileName.getName()));
for (File file : files) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
}
public static void deleteOldComponent(Context context, Set excludeNames) {
File dir = getAdvanceWallpaperDir(context);
if (!dir.exists()) {
return;
}
File[] files = dir.listFiles(fileName ->
!excludeNames.contains(fileName.getName()));
for (File file : files) {
//noinspection ResultOfMethodCallIgnored
file.delete();
NativeFileHelperKt.clearNativeFiles(context, file.getAbsolutePath());
}
}
public static void deleteOldWallpapers(Context context, String... excludeIds) {
File directory = new File(context.getFilesDir(), WALLPAPER_FOLDER);
if (!directory.exists()) {
return;
}
Set namesSet = new HashSet<>();
for (String wallpaperId : excludeIds) {
namesSet.add(generateFileName(wallpaperId));
}
File[] files = directory.listFiles(fileName ->
!namesSet.contains(fileName.getName()));
for (File file : files) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
}
public static boolean ensureWallpaperChecksumValid(Context context,
String checksum, String wallpaperId) {
File directory = new File(context.getFilesDir(), WALLPAPER_FOLDER);
if (!directory.exists()) {
return false;
}
File file = new File(directory, generateFileName(wallpaperId));
String computedChecksum = ChecksumUtil.getChecksum(file);
if (TextUtils.equals(checksum, computedChecksum)) {
return true;
}
//noinspection ResultOfMethodCallIgnored
file.delete();
return false;
}
public static boolean ensureChecksumValid(Context context,
String checksum, String filePath) {
File file = new File(filePath);
if (!file.exists()) {
return false;
}
String computedChecksum = ChecksumUtil.getChecksum(file);
if (TextUtils.equals(checksum, computedChecksum)) {
return true;
}
//noinspection ResultOfMethodCallIgnored
file.delete();
return false;
}
public static File getAdvanceWallpaperDir(Context context) {
return new File(context.getFilesDir(), ADVANCE_WALLPAPER_FOLDER);
}
public static boolean isNeedDownloadAdvanceComponent(boolean lazy, String storePath) {
return lazy && !new File(storePath).exists();
}
}
================================================
FILE: data/src/main/java/com/yalin/style/data/utils/WallpaperUtil.kt
================================================
package com.yalin.style.data.utils
import android.content.Context
import android.net.Uri
import com.yalin.style.data.repository.datasource.provider.StyleContractHelper
/**
* YaLin
* On 2017/5/26.
*/
fun notifyChange(context: Context, uri: Uri) {
if (!StyleContractHelper.isUriCalledFromSyncAdapter(uri)) {
context.contentResolver.notifyChange(uri, null)
}
}
================================================
FILE: data/src/main/res/values/strings.xml
================================================
Style
com.yalin.style
Advance Wallpaper
Advance animation component
Featured art
A new painting every day
My Photos
Random camera photos
From your gallery
Touch to view more
================================================
FILE: data/src/main/res/values-zh-rCN/strings.xml
================================================
Style
com.yalin.style
特效动态壁纸
富含各种炫酷效果
Style
每天自动更新艺术作品
我的照片
随机更新照片
来自你的相册
点击查看更多
================================================
FILE: data/src/main/res/xml/authenticator.xml
================================================
================================================
FILE: data/src/main/res/xml/syncadapter.xml
================================================
================================================
FILE: data/src/test/java/com/yalin/data/ExampleUnitTest.java
================================================
package com.yalin.data;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: domain/.gitignore
================================================
/build
================================================
FILE: domain/build.gradle
================================================
apply plugin: 'java'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
dependencies {
compile "io.reactivex.rxjava2:rxjava:${RXJAVA_VERSION}"
compile "com.fernandocejas:arrow:${ARROW_VERSION}"
compile "javax.inject:javax.inject:${JAVAX_INJECT_VERSION}"
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/AdvanceWallpaper.java
================================================
package com.yalin.style.domain;
/**
* @author jinyalin
* @since 2017/7/28.
*/
public class AdvanceWallpaper {
public long id;
public String wallpaperId;
public String link;
public String name;
public String author;
public String iconUrl;
public String downloadUrl;
public boolean lazyDownload;
public boolean needAd;
public String providerName;
public String storePath;
public boolean isDefault;
public boolean isSelected;
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/GalleryWallpaper.java
================================================
package com.yalin.style.domain;
/**
* @author jinyalin
* @since 2017/5/24.
*/
public class GalleryWallpaper {
public long id;
public String uri;
public boolean isTreeUri;
public long dateTime;
public String location;
public boolean hasMetadata;
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/Source.java
================================================
package com.yalin.style.domain;
/**
* @author jinyalin
* @since 2017/5/22.
*/
public class Source {
public int id;
public String title;
public String description;
public int iconId;
public boolean selected;
public boolean hasSetting;
public int color;
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/Wallpaper.java
================================================
package com.yalin.style.domain;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public class Wallpaper {
public String wallpaperId;
public String imageUri;
public String title;
public String byline;
public String attribution;
public boolean canLike;
public boolean liked;
public boolean isDefault;
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/exception/DefaultErrorBundle.java
================================================
package com.yalin.style.domain.exception;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public class DefaultErrorBundle implements ErrorBundle {
private static final String DEFAULT_ERROR_MSG = "Unknown error";
private final Exception exception;
public DefaultErrorBundle(Exception exception) {
this.exception = exception;
}
@Override
public Exception getException() {
return exception;
}
@Override
public String getErrorMessage() {
return (exception != null) ? this.exception.getMessage() : DEFAULT_ERROR_MSG;
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/exception/ErrorBundle.java
================================================
package com.yalin.style.domain.exception;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public interface ErrorBundle {
Exception getException();
String getErrorMessage();
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/executor/PostExecutionThread.java
================================================
package com.yalin.style.domain.executor;
import io.reactivex.Scheduler;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public interface PostExecutionThread {
Scheduler getScheduler();
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/executor/SerialThreadExecutor.java
================================================
package com.yalin.style.domain.executor;
import java.util.concurrent.Executor;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public interface SerialThreadExecutor extends Executor {
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/executor/ThreadExecutor.java
================================================
package com.yalin.style.domain.executor;
import java.util.concurrent.Executor;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public interface ThreadExecutor extends Executor {
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/AddGalleryWallpaper.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.GalleryWallpaper;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/5/24.
*/
public class AddGalleryWallpaper extends UseCase {
private SourcesRepository sourcesRepository;
@Inject
public AddGalleryWallpaper(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable buildUseCaseObservable(Params params) {
return sourcesRepository.getWallpaperRepository()
.addGalleryWallpaperUris(params.galleryWallpaperUris);
}
public static final class Params {
private final List galleryWallpaperUris;
private Params(List galleryWallpapers) {
this.galleryWallpaperUris = galleryWallpapers;
}
public static Params addGalleryWallpaperUris(List galleryWallpapers) {
return new Params(galleryWallpapers);
}
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/DefaultObserver.java
================================================
package com.yalin.style.domain.interactor;
import io.reactivex.observers.DisposableObserver;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public class DefaultObserver extends DisposableObserver {
@Override
public void onNext(T needDownload) {
// no-op by default.
}
@Override
public void onComplete() {
// no-op by default.
}
@Override
public void onError(Throwable exception) {
// no-op by default.
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/DownloadAdvanceWallpaper.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/8/11.
*/
public class DownloadAdvanceWallpaper extends UseCase {
private SourcesRepository sourcesRepository;
@Inject
public DownloadAdvanceWallpaper(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable buildUseCaseObservable(Params params) {
return sourcesRepository.getWallpaperRepository()
.downloadAdvanceWallpaper(params.wallpaperId);
}
public static final class Params {
private final String wallpaperId;
private Params(String wallpaperId) {
this.wallpaperId = wallpaperId;
}
public static Params download(String wallpaperId) {
return new Params(wallpaperId);
}
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/ForceNow.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/6/9.
*/
public class ForceNow extends UseCase {
private SourcesRepository sourcesRepository;
@Inject
public ForceNow(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable buildUseCaseObservable(Params params) {
return sourcesRepository.getWallpaperRepository().foreNow(params.galleryWallpaperUri);
}
public static final class Params {
private final String galleryWallpaperUri;
private Params(String galleryWallpaperUri) {
this.galleryWallpaperUri = galleryWallpaperUri;
}
public static Params fromUri(String galleryWallpaperUri) {
return new Params(galleryWallpaperUri);
}
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetAdvanceWallpapers.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.AdvanceWallpaper;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/7/28.
*/
public class GetAdvanceWallpapers extends UseCase, Void> {
private SourcesRepository sourcesRepository;
@Inject
public GetAdvanceWallpapers(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable> buildUseCaseObservable(Void aVoid) {
return sourcesRepository.getWallpaperRepository().getAdvanceWallpapers();
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetGalleryUpdateInterval.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/6/9.
*/
public class GetGalleryUpdateInterval extends UseCase {
private SourcesRepository sourcesRepository;
@Inject
public GetGalleryUpdateInterval(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable buildUseCaseObservable(Void aVoid) {
return sourcesRepository.getWallpaperRepository().getGalleryUpdateInterval();
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetGalleryWallpaper.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.GalleryWallpaper;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import java.util.List;
import io.reactivex.Observable;
import javax.inject.Inject;
/**
* @author jinyalin
* @since 2017/5/24.
*/
public class GetGalleryWallpaper extends UseCase, Void> {
private SourcesRepository sourcesRepository;
@Inject
public GetGalleryWallpaper(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable> buildUseCaseObservable(Void aVoid) {
return sourcesRepository.getWallpaperRepository().getGalleryWallpapers();
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetSelectedAdvanceWallpaper.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.AdvanceWallpaper;
import com.yalin.style.domain.repository.SourcesRepository;
import javax.inject.Inject;
/**
* @author jinyalin
* @since 2017/7/28.
*/
public class GetSelectedAdvanceWallpaper {
private SourcesRepository sourcesRepository;
@Inject
public GetSelectedAdvanceWallpaper(SourcesRepository sourcesRepository) {
this.sourcesRepository = sourcesRepository;
}
public AdvanceWallpaper getSelected() {
return sourcesRepository.getWallpaperRepository().getAdvanceWallpaper();
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetSelectedSource.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.repository.SourcesRepository;
import javax.inject.Inject;
/**
* @author jinyalin
* @since 2017/7/27.
*/
public class GetSelectedSource {
private SourcesRepository sourcesRepository;
@Inject
public GetSelectedSource(SourcesRepository sourcesRepository) {
this.sourcesRepository = sourcesRepository;
}
public int getSelectedSourceId() {
return sourcesRepository.getSelectedSource();
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetSources.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.Source;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/5/22.
*/
public class GetSources extends UseCase, Void> {
private SourcesRepository sourcesRepository;
@Inject
public GetSources(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable> buildUseCaseObservable(Void a) {
return sourcesRepository.getSources();
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetWallpaper.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.Wallpaper;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/18.
*/
public class GetWallpaper extends UseCase {
private SourcesRepository sourcesRepository;
@Inject
public GetWallpaper(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable buildUseCaseObservable(Void a) {
return sourcesRepository.getWallpaperRepository().getWallpaper();
}
}
================================================
FILE: domain/src/main/java/com/yalin/style/domain/interactor/GetWallpaperCount.java
================================================
package com.yalin.style.domain.interactor;
import com.yalin.style.domain.executor.PostExecutionThread;
import com.yalin.style.domain.executor.SerialThreadExecutor;
import com.yalin.style.domain.executor.ThreadExecutor;
import com.yalin.style.domain.repository.SourcesRepository;
import javax.inject.Inject;
import io.reactivex.Observable;
/**
* @author jinyalin
* @since 2017/4/28.
*/
public class GetWallpaperCount extends UseCase {
private SourcesRepository sourcesRepository;
@Inject
public GetWallpaperCount(ThreadExecutor threadExecutor,
SerialThreadExecutor serialThreadExecutor,
PostExecutionThread postExecutionThread,
SourcesRepository sourcesRepository) {
super(threadExecutor, serialThreadExecutor, postExecutionThread);
this.sourcesRepository = sourcesRepository;
}
@Override
Observable