├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── encodings.xml ├── gradle.xml ├── jarRepositories.xml ├── markdown-navigator-enh.xml ├── markdown-navigator.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ellison │ │ └── jetpackdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ellison │ │ │ └── jetpackdemo │ │ │ ├── MainActivity.java │ │ │ ├── MyApplication.kt │ │ │ ├── appCompat │ │ │ └── DemoActivity.java │ │ │ ├── camera2 │ │ │ └── DemoActivity.java │ │ │ ├── cameraX │ │ │ ├── DemoActivityLite.java │ │ │ ├── NewCameraXActivity.kt │ │ │ ├── analysis │ │ │ │ ├── AnalysisResult.kt │ │ │ │ ├── AnalysisType.kt │ │ │ │ ├── ChooseAnalysisAdapter.kt │ │ │ │ ├── ChooseAnalysisFragment.kt │ │ │ │ ├── HuaweiScanAnalysis.kt │ │ │ │ ├── OCRAnalysis.kt │ │ │ │ ├── RealTimeAnalysis.kt │ │ │ │ └── ZXingAnalysis.kt │ │ │ ├── analyzer │ │ │ │ ├── AnalyzeCallback.kt │ │ │ │ └── MyAnalyzer.kt │ │ │ ├── capture │ │ │ │ └── MyCaptureCallback.kt │ │ │ ├── extender │ │ │ │ └── MyExtenderHelper.kt │ │ │ ├── selector │ │ │ │ └── AllCameraFilter.kt │ │ │ ├── utils │ │ │ │ ├── BeepManager.kt │ │ │ │ ├── Constants.kt │ │ │ │ └── Utils.kt │ │ │ ├── video │ │ │ │ └── MyRecordCallback.kt │ │ │ └── viewmodel │ │ │ │ └── CameraViewModel.kt │ │ │ ├── coroutines │ │ │ └── DemoActivity.kt │ │ │ ├── databinding │ │ │ ├── DemoActivity.java │ │ │ ├── MyHolder.java │ │ │ ├── OneWayBindingAdapter.java │ │ │ ├── Person.java │ │ │ └── TwoWayBindingAdapter.java │ │ │ ├── hilt │ │ │ ├── BaseActivity.kt │ │ │ ├── DemoActivity.kt │ │ │ ├── DemoBroadcastReceiver.kt │ │ │ ├── DemoFragment.kt │ │ │ ├── bean │ │ │ │ ├── Movie.kt │ │ │ │ └── MovieResponse.kt │ │ │ ├── model │ │ │ │ ├── LocalData.kt │ │ │ │ ├── RemoteData.kt │ │ │ │ ├── Repository.kt │ │ │ │ ├── analysis │ │ │ │ │ ├── AnalyseService.kt │ │ │ │ │ └── AnalysisModule.kt │ │ │ │ ├── database │ │ │ │ │ ├── MovieDao.kt │ │ │ │ │ ├── NewMovieDataBase.kt │ │ │ │ │ └── RoomModule.kt │ │ │ │ ├── interceptor │ │ │ │ │ ├── CallServerInterceptorOkHttpClient.kt │ │ │ │ │ └── LoggingInterceptorOkHttpClient.kt │ │ │ │ └── network │ │ │ │ │ ├── NetworkModule.kt │ │ │ │ │ └── NetworkService.kt │ │ │ ├── view │ │ │ │ └── MovieAdapter.kt │ │ │ └── viewmodel │ │ │ │ └── MovieViewModel.kt │ │ │ ├── lifecycle │ │ │ ├── ActivityLifecycleCallbackImpl.java │ │ │ ├── DemoActivity.java │ │ │ ├── LifeCycleObserverImpl.java │ │ │ └── TestInterface.java │ │ │ ├── liveData │ │ │ ├── DemoActivity.java │ │ │ ├── Movie.java │ │ │ ├── MovieChangeListener.java │ │ │ ├── MovieLiveData.java │ │ │ └── MovieManager.java │ │ │ ├── old │ │ │ └── DemoActivity.java │ │ │ ├── room │ │ │ ├── DemoActivity.kt │ │ │ ├── GestureListener.kt │ │ │ ├── Movie.kt │ │ │ ├── MovieAdapter.kt │ │ │ ├── MovieDao.kt │ │ │ ├── MovieDataBase.kt │ │ │ ├── MovieGestureCallback.kt │ │ │ ├── MovieViewModel.kt │ │ │ └── Utils.kt │ │ │ ├── viewBinding │ │ │ ├── DemoActivity.java │ │ │ └── DemoFragment.java │ │ │ ├── viewModel │ │ │ ├── DemoActivity.java │ │ │ ├── DemoFragment.java │ │ │ ├── OtherFragment.java │ │ │ ├── Person.java │ │ │ ├── PersonContextModel.java │ │ │ ├── PersonContextStateModel.java │ │ │ ├── PersonModel.java │ │ │ └── PersonStateModel.java │ │ │ └── viewModelBinding │ │ │ ├── DemoActivity.java │ │ │ ├── Person.java │ │ │ ├── PersonsAdapter.java │ │ │ └── PersonsContextModel.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── huawei_logo.png │ │ ├── ic_add.png │ │ ├── ic_analysis_picker_bg.xml │ │ ├── ic_camera.xml │ │ ├── ic_camera_change.xml │ │ ├── ic_camera_change_bg.xml │ │ ├── ic_camera_new.xml │ │ ├── ic_capture.xml │ │ ├── ic_capture_normal.xml │ │ ├── ic_capture_pressed.xml │ │ ├── ic_capture_record.xml │ │ ├── ic_capture_record_disabled.xml │ │ ├── ic_capture_record_normal.xml │ │ ├── ic_capture_record_pressed.xml │ │ ├── ic_capture_record_pressing.xml │ │ ├── ic_capture_record_pressing_center.xml │ │ ├── ic_capture_record_pressing_outter.xml │ │ ├── ic_filter.png │ │ ├── ic_focus_view.xml │ │ ├── ic_jetpack.jpg │ │ ├── ic_launcher_background.xml │ │ ├── ic_movie_post.jpg │ │ ├── ic_point_view.xml │ │ ├── ic_qr_code.xml │ │ ├── ic_qr_scan.xml │ │ ├── ic_qr_zone.xml │ │ ├── ic_rect_view.xml │ │ ├── ic_search.png │ │ ├── ic_sort.png │ │ ├── ic_video.xml │ │ ├── ml_logo.png │ │ ├── zxing_logo.png │ │ ├── zxing_logo_round.png │ │ └── zxing_logo_transparent.png │ │ ├── layout-land │ │ ├── activity_main.xml │ │ ├── activity_view_binding.xml │ │ ├── activity_view_model.xml │ │ ├── fragment_view_binding.xml │ │ └── fragment_view_model.xml │ │ ├── layout │ │ ├── activity_camera2.xml │ │ ├── activity_camerax_lite.xml │ │ ├── activity_coroutines.xml │ │ ├── activity_data_binding.xml │ │ ├── activity_data_binding_item.xml │ │ ├── activity_data_binding_item_one_way.xml │ │ ├── activity_hilt.xml │ │ ├── activity_hilt_item.xml │ │ ├── activity_lifecycle.xml │ │ ├── activity_live_data.xml │ │ ├── activity_main.xml │ │ ├── activity_main_test.xml │ │ ├── activity_main_test_2.xml │ │ ├── activity_old.xml │ │ ├── activity_room_db.xml │ │ ├── activity_room_db_item.xml │ │ ├── activity_view_binding.xml │ │ ├── activity_view_model.xml │ │ ├── activity_view_model_binding.xml │ │ ├── activity_view_model_binding_item.xml │ │ ├── analysis_list_item.xml │ │ ├── fragment_analysis_list.xml │ │ ├── fragment_hilt.xml │ │ ├── fragment_other_view_model.xml │ │ ├── fragment_view_binding.xml │ │ └── fragment_view_model.xml │ │ ├── menu │ │ └── room_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── raw │ │ └── beep_sound.ogg │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── ellison │ └── jetpackdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/markdown-navigator-enh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ellison Chan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ellison/jetpackdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.ellison.jetpackdemo", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 36 | 37 | 38 | 40 | 41 | 42 | 44 | 45 | 46 | 49 | 50 | 51 | 53 | 54 | 55 | 58 | 59 | 60 | 62 | 63 | 64 | 65 | 68 | 69 | 70 | 73 | 74 | 75 | 78 | 79 | 80 | 83 | 84 | 85 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/MyApplication.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo 2 | 3 | import android.app.Application 4 | import androidx.camera.core.CameraXConfig 5 | import android.util.Log 6 | import androidx.camera.camera2.Camera2Config 7 | import com.ellison.jetpackdemo.lifecycle.ActivityLifecycleCallbackImpl 8 | import dagger.hilt.android.HiltAndroidApp 9 | 10 | @HiltAndroidApp 11 | class MyApplication : Application(), CameraXConfig.Provider { 12 | private var mActivityLifecycle: ActivityLifecycleCallbacks? = null 13 | 14 | override fun getCameraXConfig(): CameraXConfig { 15 | return Camera2Config.defaultConfig() 16 | } 17 | 18 | override fun onCreate() { 19 | Log.d(TAG, "onCreate()") 20 | super.onCreate() 21 | mActivityLifecycle = ActivityLifecycleCallbackImpl() 22 | registerActivityLifecycleCallbacks(mActivityLifecycle) 23 | } 24 | 25 | override fun onTerminate() { 26 | Log.d(TAG, "onTerminate()") 27 | super.onTerminate() 28 | unregisterActivityLifecycleCallbacks(mActivityLifecycle) 29 | } 30 | 31 | companion object { 32 | @JvmField 33 | val TAG = MyApplication::class.java.simpleName 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/appCompat/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.appCompat; 2 | 3 | import android.os.Bundle; 4 | import android.preference.PreferenceGroup; 5 | 6 | import com.ellison.jetpackdemo.R; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | public class DemoActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_main_test); 16 | // setContentView(R.layout.activity_main_test_2); 17 | PreferenceGroup preferenceGroup; 18 | androidx.preference.CheckBoxPreference checkBoxPreference; 19 | 20 | androidx.preference.PreferenceFragmentCompat preferenceFragmentCompat; 21 | androidx.preference.PreferenceFragment preferenceFragment; 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analysis/AnalysisResult.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analysis 2 | 3 | import android.graphics.Rect 4 | import com.ellison.jetpackdemo.cameraX.utils.Constants 5 | 6 | data class AnalysisResult(val content: String, val zoomScale: Double, val rect: Rect) 7 | 8 | val defaultResult = AnalysisResult("", Constants.DEFAULT_ZOOM_SCALE, Rect()) -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analysis/AnalysisType.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analysis 2 | 3 | import com.ellison.jetpackdemo.R 4 | 5 | data class AnalysisType internal constructor( 6 | val logo: Int, 7 | val name: String?, 8 | val clazz: Class? 9 | ) 10 | 11 | val typeList = listOf( 12 | AnalysisType(R.drawable.huawei_logo,"Huawei ScanKit", HuaweiScanAnalysis::class.java), 13 | AnalysisType(R.drawable.zxing_logo_transparent,"Google Zxing", ZXingAnalysis::class.java), 14 | AnalysisType(R.drawable.ml_logo,"Google ML Kit", OCRAnalysis::class.java) 15 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analysis/ChooseAnalysisAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analysis 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.ellison.jetpackdemo.databinding.AnalysisListItemBinding 7 | 8 | class ChooseAnalysisAdapter( 9 | private val data: List, 10 | private val listener: AnalysisChooseListener 11 | ): RecyclerView.Adapter() { 12 | interface AnalysisChooseListener { fun onAnalysisChoose(type: AnalysisType) } 13 | 14 | private class MyViewHolder(val binding: AnalysisListItemBinding) : RecyclerView.ViewHolder(binding.root) 15 | 16 | override fun getItemCount(): Int = data.size 17 | 18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = 19 | AnalysisListItemBinding.inflate( 20 | LayoutInflater.from(parent.context), 21 | parent, 22 | false 23 | ).let { 24 | MyViewHolder(it) 25 | } 26 | 27 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 28 | val myViewHolder = holder as MyViewHolder 29 | myViewHolder.binding.text1.text = data[position].name 30 | myViewHolder.binding.logo.setImageResource(data[position].logo) 31 | 32 | holder.itemView.tag = position 33 | holder.itemView.setOnClickListener { listener.onAnalysisChoose(data[it.tag as Int]) } 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analysis/ChooseAnalysisFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analysis 2 | 3 | import android.graphics.Color 4 | import android.graphics.drawable.ColorDrawable 5 | import android.os.Bundle 6 | import android.util.Log 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.view.WindowManager 11 | import android.widget.FrameLayout 12 | import androidx.fragment.app.activityViewModels 13 | import androidx.recyclerview.widget.DividerItemDecoration 14 | import androidx.recyclerview.widget.LinearLayoutManager 15 | import androidx.recyclerview.widget.RecyclerView 16 | import com.ellison.jetpackdemo.R 17 | import com.ellison.jetpackdemo.cameraX.analysis.ChooseAnalysisAdapter.AnalysisChooseListener 18 | import com.ellison.jetpackdemo.cameraX.utils.Constants 19 | import com.ellison.jetpackdemo.cameraX.viewmodel.CameraViewModel 20 | import com.ellison.jetpackdemo.databinding.FragmentAnalysisListBinding 21 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 22 | 23 | class ChooseAnalysisFragment: BottomSheetDialogFragment() { 24 | private val viewModel: CameraViewModel by activityViewModels() 25 | 26 | override fun onCreateView( 27 | inflater: LayoutInflater, 28 | container: ViewGroup?, 29 | savedInstanceState: Bundle? 30 | ): View? { 31 | Log.d(Constants.TAG_CAMERA_UI, "onCreateView") 32 | val binding = FragmentAnalysisListBinding.inflate(inflater, container, false) 33 | initAnalysisList(binding.analysisList) 34 | return binding.root 35 | } 36 | 37 | override fun onStart() { 38 | Log.d(Constants.TAG_CAMERA, "onStart()") 39 | super.onStart() 40 | 41 | dialog?.let{ 42 | // Ensure status bar not shown. 43 | it.window?.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) 44 | 45 | it.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) 46 | val bottomView: FrameLayout = it.findViewById(com.google.android.material.R.id.design_bottom_sheet) 47 | bottomView.setBackgroundResource(R.drawable.ic_analysis_picker_bg) 48 | } 49 | } 50 | 51 | private fun initAnalysisList(recyclerView: RecyclerView) { 52 | Log.d(Constants.TAG_CAMERA, "initAnalysisList() viewModel:$viewModel") 53 | val layoutManager = LinearLayoutManager(activity) 54 | isCancelable = false 55 | 56 | recyclerView.run{ 57 | setLayoutManager(layoutManager) 58 | addItemDecoration(DividerItemDecoration(activity, DividerItemDecoration.VERTICAL)) 59 | adapter = ChooseAnalysisAdapter(typeList, object : AnalysisChooseListener { 60 | override fun onAnalysisChoose(type: AnalysisType) { 61 | Log.d(Constants.TAG_CAMERA, "onAnalysisChoose() type:$type") 62 | viewModel.chooseAnalysis(type.clazz) 63 | dismissAllowingStateLoss() 64 | } 65 | }) 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analysis/OCRAnalysis.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analysis 2 | 3 | import android.content.Context 4 | import androidx.camera.core.ImageProxy 5 | 6 | class OCRAnalysis: RealTimeAnalysis { 7 | override fun analyzeContent(imageProxy: ImageProxy, context: Context): AnalysisResult { 8 | // Todo: use google's ml kit 9 | imageProxy.close() 10 | return defaultResult 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analysis/RealTimeAnalysis.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analysis 2 | 3 | import android.content.Context 4 | import androidx.camera.core.ImageProxy 5 | 6 | interface RealTimeAnalysis { 7 | fun analyzeContent(imageProxy: ImageProxy, context: Context): AnalysisResult { return defaultResult } 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analysis/ZXingAnalysis.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analysis 2 | 3 | import android.content.Context 4 | import android.graphics.Rect 5 | import android.util.Log 6 | import androidx.camera.core.ImageProxy 7 | import com.ellison.jetpackdemo.cameraX.utils.Constants 8 | import com.google.zxing.BinaryBitmap 9 | import com.google.zxing.MultiFormatReader 10 | import com.google.zxing.PlanarYUVLuminanceSource 11 | import com.google.zxing.common.HybridBinarizer 12 | 13 | class ZXingAnalysis(): RealTimeAnalysis { 14 | private val multiFormatReader = MultiFormatReader() 15 | 16 | override fun analyzeContent(imageProxy: ImageProxy, context: Context): AnalysisResult { 17 | val byteBuffer = imageProxy.planes[0].buffer 18 | val data = ByteArray(byteBuffer.remaining()) 19 | byteBuffer[data] 20 | 21 | val width = imageProxy.width 22 | val height = imageProxy.height 23 | val source = PlanarYUVLuminanceSource( 24 | data, width, height, 0, 0, width, height, false 25 | ) 26 | 27 | val bitmap = BinaryBitmap(HybridBinarizer(source)) 28 | 29 | var result = "" 30 | try { 31 | result = multiFormatReader.decode(bitmap).text 32 | Log.d("Camera", "result:$result") 33 | } catch (e: Exception) { 34 | Log.e("Camera", "Error decoding barcode") 35 | } 36 | 37 | imageProxy.close() 38 | return AnalysisResult(result, Constants.DEFAULT_ZOOM_SCALE, Rect()) 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analyzer/AnalyzeCallback.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analyzer 2 | 3 | import com.ellison.jetpackdemo.cameraX.analysis.AnalysisResult 4 | 5 | interface AnalyzeCallback { 6 | fun onZoomPreview(scale: Double) 7 | fun onAnalyzeResult(result: AnalysisResult) 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/analyzer/MyAnalyzer.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.analyzer 2 | 3 | import android.util.Log 4 | import androidx.camera.core.ImageAnalysis.Analyzer 5 | import androidx.camera.core.ImageProxy 6 | import com.ellison.jetpackdemo.cameraX.utils.Constants 7 | import com.ellison.jetpackdemo.cameraX.viewmodel.CameraViewModel 8 | 9 | class MyAnalyzer( 10 | private val viewModel: CameraViewModel, 11 | private val callback: AnalyzeCallback 12 | ): Analyzer { 13 | override fun analyze(image: ImageProxy) { 14 | Log.d(Constants.TAG_CAMERA, "analyze() image:$image") 15 | viewModel.analysePicture(image).also { 16 | Log.d(Constants.TAG_CAMERA, "analyze() result:$it") 17 | 18 | if (Constants.DEFAULT_ZOOM_SCALE != it.zoomScale 19 | && Constants.MIN_ZOOM_SCALE != it.zoomScale 20 | ) { 21 | callback.onZoomPreview(it.zoomScale) 22 | } else { 23 | callback.onAnalyzeResult(it) 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/capture/MyCaptureCallback.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.capture 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import android.widget.Toast 6 | import androidx.camera.core.ImageCapture 7 | import androidx.camera.core.ImageCaptureException 8 | import com.ellison.jetpackdemo.cameraX.utils.Constants 9 | 10 | class MyCaptureCallback( 11 | private val picCount: Int, 12 | private val context: Context 13 | ): ImageCapture.OnImageSavedCallback { 14 | override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { 15 | val uri = outputFileResults.savedUri 16 | Log.d( 17 | Constants.TAG_CAMERA, "outputFileResults:$uri picCount:$picCount" 18 | ) 19 | 20 | val path = if (uri != null) (" @ " + uri.path) else "none" 21 | 22 | Toast.makeText( 23 | context, "Picture got:$path.", Toast.LENGTH_SHORT 24 | ).show() 25 | } 26 | 27 | override fun onError(exception: ImageCaptureException) { 28 | Log.d(Constants.TAG_CAMERA, "onError:${exception.imageCaptureError}") 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/extender/MyExtenderHelper.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.extender 2 | 3 | import android.util.Log 4 | import androidx.camera.core.CameraSelector 5 | import androidx.camera.core.ImageCapture 6 | import androidx.camera.core.Preview 7 | import androidx.camera.extensions.* 8 | import com.ellison.jetpackdemo.cameraX.utils.Constants 9 | 10 | class MyExtenderHelper { 11 | fun setPreviewExtender(builder: Preview.Builder, cameraSelector: CameraSelector) { 12 | BeautyPreviewExtender.create(builder).let { 13 | if (it.isExtensionAvailable(cameraSelector)) { 14 | // Enable the extension if available. 15 | Log.d(Constants.TAG_CAMERA, "beauty preview extension enable") 16 | it.enableExtension(cameraSelector) 17 | } else { 18 | Log.d(Constants.TAG_CAMERA, "beauty preview extension not available") 19 | } 20 | } 21 | } 22 | 23 | fun setCaptureExtender(builder: ImageCapture.Builder, cameraSelector: CameraSelector) { 24 | val nightImageCaptureExtender = NightImageCaptureExtender.create(builder) 25 | if (nightImageCaptureExtender.isExtensionAvailable(cameraSelector)) { 26 | // Enable the extension if available. 27 | Log.d(Constants.TAG_CAMERA, "night capture extension enable") 28 | nightImageCaptureExtender.enableExtension(cameraSelector) 29 | } else { 30 | Log.d(Constants.TAG_CAMERA, "night capture extension not available") 31 | } 32 | 33 | val bokehImageCapture = BokehImageCaptureExtender.create(builder); 34 | if (bokehImageCapture.isExtensionAvailable(cameraSelector)) { 35 | // Enable the extension if available. 36 | Log.d(Constants.TAG_CAMERA, "hdr extension enable"); 37 | bokehImageCapture.enableExtension(cameraSelector); 38 | } else { 39 | Log.d(Constants.TAG_CAMERA, "hdr extension not available"); 40 | } 41 | 42 | val hdrImageCaptureExtender = HdrImageCaptureExtender.create(builder); 43 | if (hdrImageCaptureExtender.isExtensionAvailable(cameraSelector)) { 44 | // Enable the extension if available. 45 | Log.d(Constants.TAG_CAMERA, "night extension enable"); 46 | hdrImageCaptureExtender.enableExtension(cameraSelector); 47 | } else { 48 | Log.d(Constants.TAG_CAMERA, "night extension not available"); 49 | } 50 | 51 | val beautyImageCaptureExtender = BeautyImageCaptureExtender.create(builder); 52 | if (beautyImageCaptureExtender.isExtensionAvailable(cameraSelector)) { 53 | // Enable the extension if available. 54 | Log.d(Constants.TAG_CAMERA, "beauty extension enable"); 55 | beautyImageCaptureExtender.enableExtension(cameraSelector); 56 | } else { 57 | Log.d(Constants.TAG_CAMERA, "beauty extension not available"); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/selector/AllCameraFilter.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.selector 2 | 3 | import android.util.Log 4 | import androidx.camera.core.CameraFilter 5 | import androidx.camera.core.CameraInfo 6 | import androidx.camera.core.CameraSelector 7 | import androidx.camera.core.impl.CameraInfoInternal 8 | import androidx.camera.core.impl.CameraInternal 9 | import java.util.LinkedHashSet 10 | 11 | class AllCameraFilter: CameraFilter { 12 | override fun filter(cameraInfos: MutableList): MutableList { 13 | val result: MutableList = mutableListOf() 14 | for (cameraInfo in cameraInfos) { 15 | Log.d( 16 | "Camera", "cameraInfo" + 17 | " implementationType:${cameraInfo.implementationType}" + 18 | " state:${cameraInfo.exposureState}" + 19 | " sensorRotationDegrees:${cameraInfo.sensorRotationDegrees}" 20 | ) 21 | 22 | val id = (cameraInfo as CameraInfoInternal).cameraId 23 | Log.d("Camera", "cameraInfo id:$id") 24 | 25 | // Specify the camera id that U need, such as front camera which id is 0. 26 | if (CameraSelector.LENS_FACING_FRONT.equals(id)) { 27 | Log.d("Camera", "cameraInfo add") 28 | result.add(cameraInfo) 29 | } 30 | } 31 | return result 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/utils/BeepManager.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.utils 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.media.AudioManager 6 | import android.media.MediaPlayer 7 | import android.media.MediaPlayer.OnCompletionListener 8 | import android.os.Vibrator 9 | import android.util.Log 10 | import java.io.Closeable 11 | import java.io.IOException 12 | 13 | class BeepManager(private val activity: Activity) : OnCompletionListener, 14 | MediaPlayer.OnErrorListener, Closeable { 15 | 16 | private var mediaPlayer: MediaPlayer? = null 17 | private var playBeep = false 18 | private var vibrate = false 19 | 20 | fun setPlayBeep(playBeep: Boolean) { 21 | this.playBeep = playBeep 22 | updatePrefs() 23 | } 24 | 25 | fun setVibrate(vibrate: Boolean) { 26 | this.vibrate = vibrate 27 | } 28 | 29 | @Synchronized 30 | fun updatePrefs() { 31 | if (playBeep && mediaPlayer == null) { 32 | activity.volumeControlStream = AudioManager.STREAM_MUSIC 33 | mediaPlayer = buildMediaPlayer(activity) 34 | } 35 | } 36 | 37 | @Synchronized 38 | fun playBeepSoundAndVibrate() { 39 | Log.d(Constants.TAG_BEEP, "playBeepSoundAndVibrate playBeep:$playBeep mediaPlayer:$mediaPlayer") 40 | if (playBeep && mediaPlayer != null) { 41 | mediaPlayer?.start() 42 | } 43 | if (vibrate) { 44 | val vibrator = activity 45 | .getSystemService(Context.VIBRATOR_SERVICE) as Vibrator 46 | vibrator.vibrate(Constants.VIBRATE_DURATION) 47 | } 48 | } 49 | 50 | private fun buildMediaPlayer(activity: Context): MediaPlayer { 51 | val mediaPlayer = MediaPlayer() 52 | mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC) 53 | mediaPlayer.setOnCompletionListener(this) 54 | mediaPlayer.setOnErrorListener(this) 55 | return try { 56 | val file = activity.getResources().openRawResourceFd(Constants.BEEP_OGG_FILE); 57 | // .openRawResourceFd(R.raw.beep); 58 | file.use { file -> 59 | mediaPlayer.setDataSource( 60 | file.getFileDescriptor(), 61 | file.getStartOffset(), 62 | file.getLength()); 63 | } 64 | mediaPlayer.setVolume(Constants.BEEP_VOLUME, Constants.BEEP_VOLUME) 65 | mediaPlayer.prepare() 66 | mediaPlayer 67 | } catch (ioe: IOException) { 68 | Log.e(Constants.TAG_BEEP, "media play error$ioe") 69 | mediaPlayer.release() 70 | mediaPlayer 71 | } 72 | } 73 | 74 | override fun onCompletion(mp: MediaPlayer) { 75 | // When the beep has finished playing, rewind to queue up another one. 76 | mp.seekTo(0) 77 | } 78 | 79 | @Synchronized 80 | override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { 81 | Log.e(Constants.TAG_BEEP, "onError what:$what extra:$extra") 82 | if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { 83 | // No need finish although media player got error. 84 | // activity.finish(); 85 | } else { 86 | // possibly media player error, so release and recreate 87 | mp.release() 88 | updatePrefs() 89 | } 90 | return true 91 | } 92 | 93 | @Synchronized 94 | override fun close() { 95 | mediaPlayer?.release() 96 | } 97 | 98 | init { 99 | updatePrefs() 100 | } 101 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/utils/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.utils 2 | 3 | import com.ellison.jetpackdemo.R 4 | 5 | class Constants { 6 | companion object { 7 | const val TAG_CAMERA = "Camera" 8 | const val TAG_CAMERA_UI = "CameraUI" 9 | const val TAG_BEEP = "beep" 10 | 11 | const val MIN_ZOOM_SCALE = 0.0 12 | const val MIDDLE_ZOOM_SCALE = 0.5 13 | const val MAX_ZOOM_SCALE = 1.0 14 | const val DEFAULT_ZOOM_SCALE = 1.0 15 | 16 | const val ANALYSIS_RESULT_SHOW_DURATION = 1500L 17 | const val ANALYSIS_HOLDING_DURATION = 3000L 18 | 19 | const val BEEP_VOLUME = 0.05f 20 | const val VIBRATE_DURATION = 200L 21 | const val BEEP_OGG_FILE = R.raw.beep_sound 22 | 23 | const val REQUEST_CAMERA = 20 24 | const val REQUEST_STORAGE = 30 25 | const val REQUEST_STORAGE_BINDING = 35 26 | const val REQUEST_STORAGE_VIDEO = 40 27 | const val REQUEST_STORAGE_VIDEO_BINDING = 45 28 | 29 | const val CAPTURED_FILE_NAME = "captured_picture" 30 | const val RECORDED_FILE_NAME = "recorded_video" 31 | const val RECORDED_FILE_NAME_END = "video/mp4" 32 | 33 | const val TAG_FRAGMENT_CHOOSE_ANALYSIS = "choose_analysis_screen" 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/utils/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.utils 2 | 3 | import android.graphics.Point 4 | import android.graphics.Rect 5 | import android.util.Log 6 | import android.view.View 7 | import androidx.camera.core.ImageProxy 8 | 9 | class Utils { 10 | companion object { 11 | // fun convertRectToPoint(proxy: ImageProxy, rect: Rect): Point { 12 | fun convertRectToPoint(rect: Rect, preview: View): Point { 13 | Log.d(Constants.TAG_CAMERA_UI, "convertRectToCenter" 14 | +" rect:$rect centerX:${rect.centerX()} centerY:${rect.centerY()}" 15 | // +" proxy:{width:${proxy.width} height:${proxy.height}}" 16 | +" previewView:{width:${preview.width} height:${preview.height}}") 17 | if (rect.isEmpty) { 18 | return Point() 19 | } 20 | 21 | return Point( 22 | preview.width - rect.centerY(), 23 | rect.centerX() 24 | ) 25 | } 26 | 27 | fun convertPointsToCenter(proxy: ImageProxy, points: Array?, preview: View): Point { 28 | Log.d(Constants.TAG_CAMERA_UI, "convertPointsToCenter" 29 | +" points:${points.contentToString()}" 30 | +" proxy:{width:${proxy.width} height:${proxy.height}}" 31 | +" previewView:{width:${preview.width} height:${preview.height}}") 32 | 33 | if (points == null || points.isEmpty()) 34 | return Point() 35 | 36 | // Points[0]: right bottom 37 | // Points[1]: left bottom 38 | // Points[2]: left up 39 | // Points[3]: right up 40 | 41 | val xOffset = (points[0].x - points[2].x) / 2 42 | val yOffset = (points[0].y - points[2].y) / 2 43 | 44 | // val xScaleFactor = binding.previewView.width.toFloat() / proxy.width.toFloat() 45 | // val yScaleFactor = binding.previewView.height.toFloat() / proxy.height.toFloat() 46 | // Log.d(Constants.TAG_CAMERA_UI, "xScaleFactor:$xScaleFactor yScaleFactor:$yScaleFactor") 47 | 48 | return Point(preview.width - yOffset - points[2].y, xOffset + points[2].x) 49 | // return Point(xOffset + points[2].x, yOffset + points[2].y) 50 | // return Point(yOffset + points[2].y, xOffset + points[2].x) 51 | // return Point( 52 | // ((xOffset + points[2].x) * xScaleFactor).toInt(), 53 | // ((yOffset + points[2].y) * yScaleFactor).toInt() 54 | // ) 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/video/MyRecordCallback.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.video 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import android.widget.Toast 6 | import androidx.camera.core.VideoCapture 7 | import com.ellison.jetpackdemo.cameraX.utils.Constants 8 | 9 | class MyRecordCallback( 10 | private val context: Context, 11 | private val listener: () -> Unit 12 | ): VideoCapture.OnVideoSavedCallback { 13 | override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) { 14 | Log.d( 15 | Constants.TAG_CAMERA, "onVideoSaved outputFileResults:" 16 | + outputFileResults.savedUri!!.path 17 | ) 18 | Toast.makeText( 19 | context, 20 | "Video got" + (if (outputFileResults.savedUri != null) " @ " + outputFileResults.savedUri!! 21 | .path else "") 22 | + ".", Toast.LENGTH_LONG 23 | ).show() 24 | listener.invoke() 25 | } 26 | 27 | override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) { 28 | Log.d( 29 | Constants.TAG_CAMERA, "onError videoCaptureError:" 30 | + videoCaptureError + " message:" + message, cause 31 | ) 32 | listener.invoke() 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/cameraX/viewmodel/CameraViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.cameraX.viewmodel 2 | 3 | import android.app.Application 4 | import androidx.camera.core.ImageProxy 5 | import androidx.lifecycle.AndroidViewModel 6 | import androidx.lifecycle.MutableLiveData 7 | import com.ellison.jetpackdemo.cameraX.analysis.* 8 | 9 | class CameraViewModel(application: Application) : AndroidViewModel(application) { 10 | private lateinit var pictureAnalysis: RealTimeAnalysis 11 | val analysisLiveData = MutableLiveData() 12 | 13 | fun chooseAnalysis(clazz: Class?) { 14 | pictureAnalysis = clazz?.newInstance() ?: ZXingAnalysis() 15 | analysisLiveData.value = true 16 | } 17 | 18 | fun analysePicture(imageProxy: ImageProxy): AnalysisResult = 19 | pictureAnalysis.analyzeContent(imageProxy, getApplication()) 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/databinding/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.databinding; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | import android.view.View; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | import androidx.annotation.Nullable; 11 | import androidx.appcompat.app.AppCompatActivity; 12 | import androidx.recyclerview.widget.LinearLayoutManager; 13 | 14 | public class DemoActivity extends AppCompatActivity { 15 | ActivityDataBindingBinding binding; 16 | private List mPersons = Arrays.asList(new Person[] { 17 | new Person("18", "Audi"), 18 | new Person("7", "Benz"), 19 | new Person("24", "Cadillac"), 20 | new Person("1", "DS")}); 21 | 22 | @Override 23 | protected void onCreate(@Nullable Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | 26 | // binding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding); 27 | binding = ActivityDataBindingBinding.inflate(getLayoutInflater()); 28 | setContentView(binding.getRoot()); 29 | binding.setResponder(new EventResponder()); 30 | } 31 | 32 | // Event binding. 33 | public class EventResponder { 34 | public void onClick(View view) { 35 | Log.d("dataBinding", "onClick() view:" + view); 36 | if (view == binding.testGet) { 37 | Log.d("dataBinding", "onClick() GET INFO"); 38 | getInfo(); 39 | } else if (view == binding.testUpdate) { 40 | Log.d("dataBinding", "onClick() UPDATE INFO"); 41 | updateInfo(); 42 | } 43 | } 44 | 45 | public void onClickUpdate() { 46 | Log.d("dataBinding", "onClickUpdate() UPDATE INFO"); 47 | updateInfo(); 48 | } 49 | } 50 | 51 | private void getInfo() { 52 | binding.testPersonList.setLayoutManager(new LinearLayoutManager(this)); 53 | binding.testPersonList.setAdapter(new TwoWayBindingAdapter(mPersons)); 54 | 55 | binding.testPersonListOneWay.setLayoutManager(new LinearLayoutManager(this)); 56 | binding.testPersonListOneWay.setAdapter(new OneWayBindingAdapter(mPersons)); 57 | } 58 | 59 | private void updateInfo() { 60 | mPersons.get(0).setAge("1"); 61 | mPersons.get(3).setAge("100"); 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/databinding/MyHolder.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.databinding; 2 | 3 | import androidx.databinding.ViewDataBinding; 4 | import androidx.recyclerview.widget.RecyclerView; 5 | 6 | public class MyHolder extends RecyclerView.ViewHolder { 7 | ViewDataBinding binding; 8 | 9 | public MyHolder(ViewDataBinding binding) { 10 | super(binding.getRoot()); 11 | this.binding = binding; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/databinding/OneWayBindingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.databinding; 2 | 3 | import android.util.Log; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | import android.widget.CompoundButton; 7 | 8 | import java.util.List; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | public class OneWayBindingAdapter extends TwoWayBindingAdapter { 14 | public OneWayBindingAdapter(List personList) { 15 | super(personList); 16 | Log.d("dataBinding", "OneWayBindingAdapter#OneWayBindingAdapter() personList:" + personList); 17 | } 18 | 19 | @NonNull 20 | @Override 21 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 22 | Log.d("dataBinding", "OneWayBindingAdapter#onCreateViewHolder() parent:" + parent); 23 | return new MyHolder(ActivityDataBindingItemOneWayBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); 24 | } 25 | 26 | @Override 27 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 28 | Log.d("dataBinding", "OneWayBindingAdapter#onBindViewHolder() holder:" + holder + " position:" + position); 29 | ((ActivityDataBindingItemOneWayBinding) ((MyHolder) holder).binding).setPerson(personList.get(position)); 30 | ((ActivityDataBindingItemOneWayBinding) ((MyHolder) holder).binding).setCheckResponder(new CheckResponder() { 31 | @Override 32 | public void onCheckChanged(CompoundButton buttonView, boolean isChecked) { 33 | Log.d("dataBinding", "OneWayBindingAdapter#onCheckChanged() buttonView:" + buttonView + " isChecked:" + isChecked); 34 | if (buttonView == ((ActivityDataBindingItemOneWayBinding) ((MyHolder) holder).binding).testCheck) { 35 | Log.d("dataBinding", "OneWayBindingAdapter#onCheckChanged() BTN & CHANGE COLOR"); 36 | personList.get(position).setChecked(isChecked); 37 | } 38 | } 39 | }); 40 | } 41 | 42 | // Event binding. 43 | public static class CheckResponder { 44 | public void onCheckChanged(CompoundButton buttonView, boolean isChecked) { 45 | Log.d("dataBinding", "CheckResponder#onCheckChanged() buttonView:" + buttonView + " isChecked:" + isChecked); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/databinding/Person.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.databinding; 2 | 3 | import androidx.databinding.BaseObservable; 4 | import androidx.databinding.Bindable; 5 | import androidx.databinding.library.baseAdapters.BR; 6 | 7 | public class Person extends BaseObservable { 8 | private String name; 9 | private String age; 10 | private boolean adult; 11 | private boolean checked; 12 | 13 | public Person(String age, String name) { 14 | this.age = age; 15 | this.name = name; 16 | adult = this.age != null && Integer.valueOf(this.age) >= 18; 17 | } 18 | 19 | @Bindable 20 | public String getAge() { 21 | return age; 22 | } 23 | 24 | public void setAge(String age) { 25 | this.age = age; 26 | notifyPropertyChanged(BR.age); 27 | setAdult(); 28 | } 29 | 30 | @Bindable 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | notifyPropertyChanged(BR.name); 38 | } 39 | 40 | @Bindable 41 | public boolean isAdult() { 42 | return adult; 43 | } 44 | 45 | public void setAdult() { 46 | adult = this.age != null && Integer.valueOf(this.age) >= 18; 47 | notifyPropertyChanged(BR.adult); 48 | } 49 | 50 | @Bindable 51 | public boolean isChecked() { 52 | return checked; 53 | } 54 | 55 | public void setChecked(boolean checked) { 56 | this.checked = checked; 57 | notifyPropertyChanged(BR.checked); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "Person{" + 63 | "name='" + name + '\'' + 64 | ", age='" + age + '\'' + 65 | ", adult=" + adult + 66 | ", checked=" + checked + 67 | '}'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/databinding/TwoWayBindingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.databinding; 2 | 3 | import android.util.Log; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import java.util.List; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | public class TwoWayBindingAdapter extends RecyclerView.Adapter { 13 | protected List personList; 14 | 15 | public TwoWayBindingAdapter(List personList) { 16 | this.personList = personList; 17 | Log.d("dataBinding", "TwoWayBindingAdapter#TwoWayBindingAdapter() personList:" + personList); 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 23 | Log.d("dataBinding", "TwoWayBindingAdapter#onCreateViewHolder() parent:" + parent); 24 | return new MyHolder(ActivityDataBindingItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); 25 | } 26 | 27 | @Override 28 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 29 | Log.d("dataBinding", "TwoWayBindingAdapter#onBindViewHolder() holder:" + holder + " position:" + position); 30 | ((ActivityDataBindingItemBinding) ((MyHolder) holder).binding).setPerson(personList.get(position)); 31 | } 32 | 33 | @Override 34 | public int getItemCount() { 35 | return personList.size(); 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import dagger.hilt.android.AndroidEntryPoint 6 | 7 | @AndroidEntryPoint 8 | open class BaseActivity() : AppCompatActivity() -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/DemoActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import android.widget.Toast 6 | import androidx.activity.viewModels 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.appcompat.widget.SearchView 9 | import androidx.recyclerview.widget.DividerItemDecoration 10 | import androidx.recyclerview.widget.GridLayoutManager 11 | import com.ellison.jetpackdemo.R 12 | import com.ellison.jetpackdemo.databinding.ActivityHiltBinding 13 | import com.ellison.jetpackdemo.hilt.bean.Movie 14 | import com.ellison.jetpackdemo.hilt.bean.MovieResponse 15 | import com.ellison.jetpackdemo.hilt.view.MovieAdapter 16 | import com.ellison.jetpackdemo.hilt.viewmodel.MovieViewModel 17 | import dagger.hilt.android.AndroidEntryPoint 18 | 19 | // @AndroidEntryPoint 20 | class DemoActivity : BaseActivity() { 21 | private val movieViewModel: MovieViewModel by viewModels() 22 | private lateinit var movieAdapter: MovieAdapter 23 | private lateinit var binding: ActivityHiltBinding 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | binding = ActivityHiltBinding.inflate(layoutInflater) 28 | setContentView(binding.root) 29 | 30 | binding.search.isSubmitButtonEnabled = true 31 | binding.search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { 32 | override fun onQueryTextSubmit(query: String): Boolean { 33 | Log.d("Hilt", "onQueryTextSubmit() got query:$query") 34 | movieViewModel.searchMovie(query, this@DemoActivity) { response: MovieResponse> -> 35 | Log.d("Hilt", "onChanged() got response:$response") 36 | 37 | if (response.Response.toBoolean() && response.Search != null) { 38 | bindRecyclerView(response.Search) 39 | } else { 40 | Toast.makeText( 41 | this@DemoActivity, 42 | "Got no movie", 43 | Toast.LENGTH_SHORT 44 | ).show() 45 | } 46 | } 47 | return false 48 | } 49 | 50 | override fun onQueryTextChange(newText: String): Boolean { 51 | return false 52 | } 53 | }) 54 | 55 | // supportFragmentManager.beginTransaction() 56 | // .add(R.id.container, DemoFragment::class.java, null) 57 | // .commit() 58 | } 59 | 60 | private fun bindRecyclerView(movieList: List) { 61 | movieAdapter = movieViewModel.movieAdapter 62 | movieAdapter.movieList = movieList 63 | movieAdapter.movieViewModel = movieViewModel 64 | 65 | binding.movieList.layoutManager = GridLayoutManager(binding.movieList.context, 2) 66 | binding.movieList.addItemDecoration(DividerItemDecoration(binding.movieList.context, 67 | DividerItemDecoration.VERTICAL)) 68 | binding.movieList.adapter = movieAdapter 69 | } 70 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/DemoBroadcastReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import com.ellison.jetpackdemo.hilt.model.network.NetworkService 7 | import dagger.hilt.android.AndroidEntryPoint 8 | import javax.inject.Inject 9 | 10 | @AndroidEntryPoint 11 | class DemoBroadcastReceiver: BroadcastReceiver() { 12 | @Inject lateinit var networkService: NetworkService 13 | override fun onReceive(context: Context?, intent: Intent?) { 14 | TODO("Not yet implemented") 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/DemoFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import android.view.View 6 | import androidx.fragment.app.Fragment 7 | // import android.app.Fragment 8 | import androidx.fragment.app.activityViewModels 9 | 10 | import com.ellison.jetpackdemo.R 11 | import com.ellison.jetpackdemo.hilt.bean.Movie 12 | import com.ellison.jetpackdemo.hilt.bean.MovieResponse 13 | import com.ellison.jetpackdemo.hilt.viewmodel.MovieViewModel 14 | 15 | import dagger.hilt.android.AndroidEntryPoint 16 | 17 | @AndroidEntryPoint 18 | class DemoFragment : Fragment(R.layout.fragment_hilt) { 19 | private val movieViewModel: MovieViewModel by activityViewModels() 20 | 21 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 22 | super.onViewCreated(view, savedInstanceState) 23 | retainInstance = true 24 | movieViewModel.searchMovie("", requireActivity()) { 25 | response: MovieResponse> -> 26 | Log.d("Hilt", "onChanged() got response:$response") 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/bean/Movie.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.bean 2 | 3 | import androidx.databinding.BaseObservable 4 | import androidx.databinding.Bindable 5 | import androidx.databinding.library.baseAdapters.BR 6 | import androidx.room.ColumnInfo 7 | import androidx.room.Entity 8 | import androidx.room.PrimaryKey 9 | 10 | @Entity 11 | class Movie(@ColumnInfo(name = "movie_name", defaultValue = "Harry Potter") 12 | private var Title: String, 13 | @ColumnInfo(name = "movie_year", defaultValue = "1991") 14 | private var Year: String, 15 | @ColumnInfo(name = "movie_id", defaultValue = "imdb324523") 16 | var imdbID: String, 17 | @ColumnInfo(name = "movie_type", defaultValue = "Movie") 18 | var Type: String, 19 | @ColumnInfo(name = "movie_poster", defaultValue = "https://ddd/dad.img") 20 | var Poster: String 21 | ) : BaseObservable() { 22 | override fun toString(): String { 23 | return "Movie(Title='$Title', Year='$Year', imdbID='$imdbID', Type='$Type')" 24 | } 25 | 26 | @JvmField 27 | @PrimaryKey(autoGenerate = true) 28 | var id = 0 29 | 30 | @Bindable 31 | fun getTitle(): String { 32 | return Title 33 | } 34 | 35 | fun setTitle(title: String) { 36 | Title = title 37 | notifyPropertyChanged(BR.title) 38 | } 39 | 40 | @Bindable 41 | fun getYear(): String { 42 | return Year 43 | } 44 | 45 | fun setYear(year: String) { 46 | Year = year 47 | notifyPropertyChanged(BR.year) 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/bean/MovieResponse.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.bean 2 | 3 | data class MovieResponse( 4 | var TotalResults: String = "0", 5 | var Response: String = "false", 6 | var Error: String = "null", 7 | var Search: T 8 | ) { 9 | override fun toString(): String { 10 | return "(TotalResults=$TotalResults,\nResponse=$Response,\nError=$Error,\nSearch=$Search)" 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/LocalData.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model 2 | 3 | import android.util.Log 4 | import com.ellison.jetpackdemo.hilt.bean.Movie 5 | import com.ellison.jetpackdemo.hilt.model.analysis.AnalysisService 6 | import com.ellison.jetpackdemo.hilt.model.database.MovieDao 7 | import javax.inject.Inject 8 | 9 | class LocalData @Inject constructor(private val analysisService: AnalysisService) { 10 | private var count = 0 11 | 12 | fun analyseMovieByAI(movie: Movie): String { 13 | return analysisService.checkSelectedMovie(movie) 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/RemoteData.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model 2 | 3 | import android.util.Log 4 | import com.ellison.jetpackdemo.hilt.bean.Movie 5 | import com.ellison.jetpackdemo.hilt.bean.MovieResponse 6 | import com.ellison.jetpackdemo.hilt.model.network.NetworkService 7 | import javax.inject.Inject 8 | 9 | // class RemoteData @Inject constructor() 10 | class RemoteData @Inject constructor(private val networkService: NetworkService) { 11 | suspend fun searchMovie(keyWord: String): MovieResponse> { 12 | Log.d("Hilt", "searchMovie() networkService:$networkService") 13 | return networkService.requestSearchByCoroutines(keyWord, "19b0bce5") 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model 2 | 3 | import android.util.Log 4 | import com.ellison.jetpackdemo.hilt.bean.Movie 5 | import com.ellison.jetpackdemo.hilt.bean.MovieResponse 6 | import javax.inject.Inject 7 | 8 | class Repository @Inject constructor(private val remoteData: RemoteData, private val localData: LocalData) { 9 | suspend fun searchMovieFromNetwork(keyWord: String): MovieResponse> { 10 | Log.d("Hilt", "searchMovieFromNetwork() remoteData:$remoteData") 11 | return remoteData.searchMovie(keyWord) 12 | } 13 | 14 | fun analyseMovieByAI(movie: Movie): String { 15 | Log.d("Hilt", "analyseMovieByAI() localData:$localData movie:$movie") 16 | return localData.analyseMovieByAI(movie) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/analysis/AnalyseService.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.analysis 2 | 3 | import android.util.Log 4 | import com.ellison.jetpackdemo.hilt.bean.Movie 5 | import com.ellison.jetpackdemo.hilt.model.database.MovieDao 6 | import javax.inject.Inject 7 | 8 | interface AnalysisService { 9 | fun checkSelectedMovie(movie: Movie): String 10 | } 11 | 12 | class AnalysisServiceImpl @Inject constructor ( 13 | private val moviedao: MovieDao): AnalysisService { 14 | override fun checkSelectedMovie(movie: Movie): String { 15 | return saveMovie(movie) 16 | } 17 | 18 | private fun saveMovie(movie: Movie): String { 19 | val result = moviedao.insert(movie) 20 | Log.d("Hilt", "saveMovie result:$result"); 21 | return movie.getTitle() + ":" + result 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/analysis/AnalysisModule.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.analysis 2 | 3 | import dagger.Binds 4 | import dagger.Module 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.android.components.ActivityComponent 7 | 8 | @Module 9 | @InstallIn(ActivityComponent::class) 10 | abstract class AnalysisModule { 11 | @Binds 12 | abstract fun bindAnalysisService(analysisService: AnalysisServiceImpl): AnalysisService 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/database/MovieDao.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.database 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | 6 | import com.ellison.jetpackdemo.hilt.bean.Movie 7 | 8 | @Dao 9 | interface MovieDao { 10 | @Insert 11 | fun insert(movie: Movie?): Long? 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/database/NewMovieDataBase.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.database 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | import com.ellison.jetpackdemo.hilt.bean.Movie 6 | 7 | @Database(entities = [Movie::class], version = 1) 8 | abstract class NewMovieDataBase: RoomDatabase() { 9 | abstract fun movieDao(): MovieDao 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/database/RoomModule.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.database 2 | 3 | import android.app.Application 4 | import androidx.room.Room 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.android.components.ApplicationComponent 9 | import javax.inject.Singleton 10 | 11 | @Module 12 | @InstallIn(ApplicationComponent::class) 13 | class RoomModule { 14 | @Singleton 15 | @Provides 16 | fun provideNewMovieDataBase(application: Application): NewMovieDataBase { 17 | return Room.databaseBuilder(application, 18 | NewMovieDataBase::class.java, 19 | "hilt-movie.db") 20 | .fallbackToDestructiveMigrationFrom(1) 21 | .allowMainThreadQueries() 22 | .build() 23 | } 24 | 25 | @Singleton 26 | @Provides 27 | fun provideMovieDao(newMovieDataBase: NewMovieDataBase): MovieDao { 28 | return newMovieDataBase.movieDao() 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/interceptor/CallServerInterceptorOkHttpClient.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.interceptor 2 | 3 | import javax.inject.Qualifier 4 | 5 | @Qualifier 6 | @Retention(AnnotationRetention.BINARY) 7 | annotation class CallServerInterceptorOkHttpClient() 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/interceptor/LoggingInterceptorOkHttpClient.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.interceptor 2 | 3 | import javax.inject.Qualifier 4 | 5 | @Qualifier 6 | @Retention(AnnotationRetention.BINARY) 7 | annotation class LoggingInterceptorOkHttpClient() 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/network/NetworkModule.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.network 2 | 3 | import com.ellison.jetpackdemo.hilt.model.interceptor.CallServerInterceptorOkHttpClient 4 | import com.ellison.jetpackdemo.hilt.model.interceptor.LoggingInterceptorOkHttpClient 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.android.components.ApplicationComponent 9 | import okhttp3.OkHttpClient 10 | import okhttp3.internal.http.CallServerInterceptor 11 | import okhttp3.logging.HttpLoggingInterceptor 12 | import retrofit2.Retrofit 13 | import retrofit2.converter.gson.GsonConverterFactory 14 | import javax.inject.Singleton 15 | 16 | @Module 17 | @InstallIn(ApplicationComponent::class) 18 | class NetworkModule { 19 | @LoggingInterceptorOkHttpClient 20 | @Provides 21 | fun provideLoggingInterceptorOkHttpClient(): OkHttpClient { 22 | val logging = HttpLoggingInterceptor() 23 | logging.setLevel(HttpLoggingInterceptor.Level.BASIC) 24 | return OkHttpClient.Builder().addInterceptor(logging).build() 25 | } 26 | 27 | @CallServerInterceptorOkHttpClient 28 | @Provides 29 | fun provideCallServerInterceptorOkHttpClient(): OkHttpClient { 30 | return OkHttpClient.Builder().addInterceptor(CallServerInterceptor(false)).build() 31 | } 32 | 33 | @Provides 34 | fun provideGsonConverterFactory(): GsonConverterFactory { 35 | return GsonConverterFactory.create() 36 | } 37 | 38 | @Provides 39 | @Singleton 40 | fun provideMovieService(@LoggingInterceptorOkHttpClient okHttpClient: OkHttpClient, 41 | gsonConverterFactory: GsonConverterFactory): NetworkService { 42 | return Retrofit.Builder() 43 | .baseUrl("http://omdbapi.com/") 44 | .addConverterFactory(gsonConverterFactory) 45 | .client(okHttpClient) 46 | .build() 47 | .create(NetworkService::class.java) 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/model/network/NetworkService.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.model.network 2 | 3 | import com.ellison.jetpackdemo.hilt.bean.Movie 4 | import com.ellison.jetpackdemo.hilt.bean.MovieResponse 5 | import retrofit2.http.GET 6 | import retrofit2.http.Query 7 | 8 | interface NetworkService { 9 | @GET("http://omdbapi.com/") 10 | suspend fun requestSearchByCoroutines( 11 | @Query("s") keywords: String, 12 | @Query("apikey") apikey: String 13 | ): MovieResponse> 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/view/MovieAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.view 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import androidx.databinding.ViewDataBinding 10 | import androidx.fragment.app.FragmentActivity 11 | import androidx.recyclerview.widget.RecyclerView 12 | import com.ellison.jetpackdemo.databinding.ActivityHiltItemBinding 13 | import com.ellison.jetpackdemo.hilt.bean.Movie 14 | import com.ellison.jetpackdemo.hilt.viewmodel.MovieViewModel 15 | import dagger.hilt.android.qualifiers.ActivityContext 16 | import javax.inject.Inject 17 | 18 | class MovieAdapter @Inject constructor(@ActivityContext private val context: Context) 19 | : RecyclerView.Adapter() { 20 | lateinit var movieList: List 21 | lateinit var movieViewModel: MovieViewModel 22 | 23 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 24 | Log.d("Hilt", "MovieAdapter#onCreateViewHolder() parent:$parent") 25 | return MovieHolder(ActivityHiltItemBinding.inflate(LayoutInflater.from(parent.context), 26 | parent, false)) 27 | } 28 | 29 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 30 | Log.d("Hilt", "MovieAdapter#onBindViewHolder() holder:$holder position:$position") 31 | val itemBinding = ((holder as MovieHolder).binding as ActivityHiltItemBinding) 32 | itemBinding.movie = movieList[position] 33 | itemBinding.movieName.tag = position 34 | itemBinding.setResponder(EventResponder()) 35 | } 36 | 37 | override fun getItemCount(): Int { 38 | return movieList.size 39 | } 40 | 41 | private class MovieHolder(var binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) 42 | 43 | // Event binding. 44 | inner class EventResponder { 45 | fun onClick(view: View) { 46 | Log.d("Hilt", "onClick() view:$view") 47 | val result = movieViewModel?.analyseMovie(movieList.get(view.tag as Int)) 48 | Toast.makeText(context, result, Toast.LENGTH_SHORT).show() 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/hilt/viewmodel/MovieViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.hilt.viewmodel 2 | 3 | import android.util.Log 4 | import androidx.fragment.app.FragmentActivity 5 | import androidx.hilt.lifecycle.ViewModelInject 6 | import androidx.lifecycle.MutableLiveData 7 | import androidx.lifecycle.Observer 8 | import androidx.lifecycle.ViewModel 9 | import androidx.lifecycle.viewModelScope 10 | import com.ellison.jetpackdemo.hilt.bean.Movie 11 | import com.ellison.jetpackdemo.hilt.bean.MovieResponse 12 | import com.ellison.jetpackdemo.hilt.model.Repository 13 | import com.ellison.jetpackdemo.hilt.view.MovieAdapter 14 | import kotlinx.coroutines.CoroutineExceptionHandler 15 | import kotlinx.coroutines.Dispatchers 16 | import kotlinx.coroutines.launch 17 | 18 | class MovieViewModel @ViewModelInject constructor(private val repository: Repository, 19 | var movieAdapter: MovieAdapter 20 | ) : ViewModel() { 21 | private val resultData = MutableLiveData>>() 22 | 23 | fun searchMovie(keyWord: String, fragmentActivity: FragmentActivity, observer: Observer>>) { 24 | Log.d("Hilt", "searchMovie() repository:$repository") 25 | resultData.observe(fragmentActivity, observer) 26 | 27 | viewModelScope.launch(Dispatchers.Main + CoroutineExceptionHandler { coroutineContext, throwable -> 28 | Log.d("Hilt", "coroutine exception: $throwable") 29 | }) { 30 | Log.d("Hilt", "searchMovie() searchMovieFromNetwork keyWord:$keyWord") 31 | val response = repository.searchMovieFromNetwork(keyWord) 32 | resultData.value = response 33 | } 34 | } 35 | 36 | fun analyseMovie(movie: Movie): String { 37 | return repository.analyseMovieByAI(movie) 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/lifecycle/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.lifecycle; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | 6 | import com.ellison.jetpackdemo.databinding.ActivityLifecycleBinding; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.annotation.Nullable; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.lifecycle.DefaultLifecycleObserver; 12 | import androidx.lifecycle.GenericLifecycleObserver; 13 | import androidx.lifecycle.Lifecycle; 14 | import androidx.lifecycle.LifecycleObserver; 15 | import androidx.lifecycle.LifecycleOwner; 16 | import androidx.lifecycle.LifecycleRegistry; 17 | 18 | public class DemoActivity extends AppCompatActivity { 19 | static final String TAG = DemoActivity.class.getSimpleName(); 20 | LifecycleObserver mLifecycleObserver; 21 | private LifecycleRegistry lifecycleRegistry; 22 | 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | Log.d(TAG, "MYSELF onCreate()"); 26 | super.onCreate(savedInstanceState); 27 | 28 | ActivityLifecycleBinding binding = ActivityLifecycleBinding.inflate(getLayoutInflater()); 29 | setContentView(binding.getRoot()); 30 | 31 | getLifecycle().addObserver(new DefaultLifecycleObserver() { 32 | @Override 33 | public void onCreate(@NonNull LifecycleOwner owner) { 34 | Log.d(TAG, "DefaultLifecycleObserver#onCreate()"); 35 | } 36 | }); 37 | 38 | getLifecycle().addObserver(new GenericLifecycleObserver() { 39 | @Override 40 | public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { 41 | Log.d(TAG, "GenericLifecycleObserver#onStateChanged() source:" + source + " event:" + event); 42 | } 43 | }); 44 | 45 | mLifecycleObserver = new LifeCycleObserverImpl(getLifecycle()); 46 | getLifecycle().addObserver(mLifecycleObserver); 47 | 48 | // lifecycleRegistry = new LifecycleRegistry(this); 49 | // lifecycleRegistry.markState(Lifecycle.State.CREATED); 50 | } 51 | 52 | // @NonNull 53 | // @Override 54 | // public Lifecycle getLifecycle() { 55 | // return lifecycleRegistry; 56 | // } 57 | 58 | @Override 59 | protected void onDestroy() { 60 | Log.d(TAG, "MYSELF onDestroy()"); 61 | super.onDestroy(); 62 | getLifecycle().removeObserver(mLifecycleObserver); 63 | } 64 | 65 | private static final class MyTestInterface implements TestInterface { 66 | @Override 67 | public int getAge() { 68 | return 0; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/lifecycle/LifeCycleObserverImpl.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.lifecycle; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.lifecycle.Lifecycle; 6 | import androidx.lifecycle.LifecycleObserver; 7 | import androidx.lifecycle.OnLifecycleEvent; 8 | 9 | import static com.ellison.jetpackdemo.lifecycle.DemoActivity.TAG; 10 | 11 | public class LifeCycleObserverImpl implements LifecycleObserver { 12 | private Lifecycle lifecycle; 13 | 14 | public LifeCycleObserverImpl(Lifecycle lifecycle) { 15 | this.lifecycle = lifecycle; 16 | } 17 | 18 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) 19 | public void onCreate() { 20 | Log.d(TAG, "LifeCycleObserverImpl#onCreate()"); 21 | } 22 | 23 | @OnLifecycleEvent(Lifecycle.Event.ON_START) 24 | public void onStart() { 25 | Log.d(TAG, "LifeCycleObserverImpl#onStart()" + lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)); 26 | } 27 | 28 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 29 | public void onResume() { 30 | Log.d(TAG, "LifeCycleObserverImpl#onResume()"); 31 | } 32 | 33 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 34 | public void onPause() { 35 | Log.d(TAG, "LifeCycleObserverImpl#onPause()"); 36 | } 37 | 38 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 39 | public void onStop() { 40 | Log.d(TAG, "LifeCycleObserverImpl#onStop()"); 41 | } 42 | 43 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 44 | public void onDestory() { 45 | Log.d(TAG, "LifeCycleObserverImpl#onDestory()"); 46 | } 47 | 48 | @OnLifecycleEvent(Lifecycle.Event.ON_ANY) 49 | public void onAny() { 50 | Log.d(TAG, "LifeCycleObserverImpl#onAny()"); 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/lifecycle/TestInterface.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.lifecycle; 2 | 3 | public interface TestInterface { 4 | static final String TAG = TestInterface.class.getSimpleName(); 5 | default void getName() {} 6 | int getAge(); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/liveData/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.liveData; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | import android.view.View; 6 | 7 | import com.ellison.jetpackdemo.databinding.ActivityLiveDataBinding; 8 | 9 | import androidx.annotation.Nullable; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | 12 | public class DemoActivity extends AppCompatActivity { 13 | static final String TAG = DemoActivity.class.getSimpleName(); 14 | ActivityLiveDataBinding binding; 15 | 16 | @Override 17 | protected void onCreate(@Nullable Bundle savedInstanceState) { 18 | Log.d(TAG, "MYSELF onCreate()"); 19 | super.onCreate(savedInstanceState); 20 | 21 | binding = ActivityLiveDataBinding.inflate(getLayoutInflater()); 22 | setContentView(binding.getRoot()); 23 | 24 | binding.setResponder(new EventResponder()); 25 | } 26 | 27 | // Event binding. 28 | public class EventResponder { 29 | public void onClickUpdate() { 30 | Log.d(TAG, "onClickUpdate() UPDATE INFO"); 31 | new MovieLiveData("Rush Hour").observe(DemoActivity.this, movie -> { 32 | Log.d(TAG, "onChanged() movie:" + movie); 33 | binding.testMovie.setVisibility(View.VISIBLE); 34 | binding.name.setText(movie.name); 35 | binding.type.setText(movie.type); 36 | binding.actor.setText(movie.actor); 37 | binding.post.setBackgroundResource(movie.postID); 38 | }); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/liveData/Movie.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.liveData; 2 | 3 | public class Movie { 4 | String name; 5 | String type; 6 | String actor; 7 | int postID; 8 | 9 | public Movie(String name, String type, String actor, int postID) { 10 | this.name = name; 11 | this.type = type; 12 | this.actor = actor; 13 | this.postID = postID; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/liveData/MovieChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.liveData; 2 | 3 | public interface MovieChangeListener { 4 | default void onMovieUpdated(Movie updatedMovie) {} 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/liveData/MovieLiveData.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.liveData; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.lifecycle.LiveData; 6 | import static com.ellison.jetpackdemo.liveData.DemoActivity.TAG; 7 | 8 | public class MovieLiveData extends LiveData { 9 | MovieManager movieManager; 10 | 11 | MovieChangeListener listener = new MovieChangeListener() { 12 | @Override 13 | public void onMovieUpdated(Movie updatedMovie) { 14 | Log.d(TAG, "onMovieUpdated() updatedMovie:" + updatedMovie); 15 | setValue(updatedMovie); 16 | } 17 | }; 18 | 19 | public MovieLiveData(String name) { 20 | movieManager = new MovieManager(name); 21 | } 22 | 23 | @Override 24 | protected void onActive() { 25 | super.onActive(); 26 | Log.d(TAG, "onActive()"); 27 | movieManager.movieChangeRequest(listener); 28 | } 29 | 30 | @Override 31 | protected void onInactive() { 32 | super.onInactive(); 33 | Log.d(TAG, "onInactive()"); 34 | movieManager.movieChangeDrop(listener); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/liveData/MovieManager.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.liveData; 2 | 3 | import com.ellison.jetpackdemo.R; 4 | 5 | public class MovieManager { 6 | String name; 7 | 8 | public MovieManager(String name) { 9 | this.name = name; 10 | } 11 | 12 | void movieChangeRequest(MovieChangeListener listener) { 13 | listener.onMovieUpdated(new Movie(name, "Action", "Jackie Chan", R.drawable.ic_movie_post)); 14 | } 15 | 16 | void movieChangeDrop(MovieChangeListener listener) { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/old/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.old; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.ellison.jetpackdemo.R; 6 | 7 | import androidx.annotation.Nullable; 8 | import android.app.Activity; 9 | 10 | public class DemoActivity extends Activity { 11 | @Override 12 | protected void onCreate(@Nullable Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_old); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/room/DemoActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.room 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.util.Log 6 | import android.view.Menu 7 | import android.view.MenuItem 8 | import androidx.appcompat.app.AppCompatActivity 9 | import androidx.appcompat.widget.SearchView 10 | import androidx.lifecycle.ViewModelProvider 11 | import com.ellison.jetpackdemo.R 12 | import com.ellison.jetpackdemo.databinding.ActivityRoomDbBinding 13 | 14 | class DemoActivity : AppCompatActivity() { 15 | private var movieViewModel: MovieViewModel? = null 16 | private var binding: ActivityRoomDbBinding? = null 17 | private var movieList: List? = null 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | 22 | binding = ActivityRoomDbBinding.inflate(layoutInflater) 23 | setContentView(binding!!.root) 24 | binding!!.lifecycleOwner = this 25 | 26 | movieViewModel = ViewModelProvider(this).get(MovieViewModel::class.java) 27 | movieViewModel?.getMovieList(this, { movieList: List? -> 28 | if (movieList == null) return@getMovieList 29 | Log.d(MovieDataBase.TAG, "onChanged() movieList:$movieList") 30 | 31 | this.movieList = movieList 32 | binding?.setMovieList(movieList) 33 | }) 34 | } 35 | 36 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 37 | menuInflater.inflate(R.menu.room_menu, menu) 38 | 39 | val myActionMenuItem = menu.findItem(R.id.action_search) 40 | val searchView = myActionMenuItem.actionView as SearchView 41 | 42 | searchView.isSubmitButtonEnabled = true 43 | searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { 44 | override fun onQueryTextSubmit(query: String): Boolean { 45 | Log.d(MovieDataBase.TAG, "onQueryTextSubmit() query:$query") 46 | 47 | if (!TextUtils.isEmpty(query) && movieViewModel != null) { 48 | movieViewModel!!.searchMovie(this@DemoActivity, { movieList: List? -> 49 | Log.d(MovieDataBase.TAG, "onChanged() movieList:$movieList") 50 | binding?.setMovieList(movieList) 51 | }, query) 52 | } 53 | return false 54 | } 55 | 56 | override fun onQueryTextChange(newText: String): Boolean { 57 | Log.d(MovieDataBase.TAG, "onQueryTextChange() newText:$newText") 58 | if (TextUtils.isEmpty(newText)) { 59 | binding?.setMovieList(movieList) 60 | } 61 | return true 62 | } 63 | }) 64 | return true 65 | } 66 | 67 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 68 | if (movieViewModel == null) return false 69 | 70 | if (R.id.action_filter == item.itemId) { 71 | movieViewModel!!.filterMovieByScore(this, { movieList: List? -> 72 | Log.d(MovieDataBase.TAG, "onOptionsItemSelected sort onChanged() movieList:$movieList") 73 | binding?.setMovieList(movieList) 74 | }, 7.0) 75 | return true 76 | } else if (R.id.action_add == item.itemId) { 77 | movieViewModel!!.getMovie(this, { movie: Movie? -> 78 | val newMovie = Movie("Kungfu kid", "Jackie Chan", 2011, 7.0) 79 | Log.d(MovieDataBase.TAG, "onOptionsItemSelected add onChanged() movie:$movie") 80 | if (movie != null) { 81 | movieViewModel!!.addDeleteMovie(this, null, newMovie, movie) 82 | } 83 | }, 1) 84 | return true 85 | } 86 | return false 87 | } 88 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/room/GestureListener.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.room 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | interface GestureListener { 6 | fun onDeleteItem(targetHolder: RecyclerView.ViewHolder?) {} 7 | fun onSwapItem(fromHolder: RecyclerView.ViewHolder?, targetHolder: RecyclerView.ViewHolder?) {} 8 | fun onGestureDone() {} 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/room/Movie.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.room 2 | 3 | import androidx.databinding.BaseObservable 4 | import androidx.databinding.Bindable 5 | import androidx.databinding.library.baseAdapters.BR 6 | import androidx.room.ColumnInfo 7 | import androidx.room.Entity 8 | import androidx.room.Ignore 9 | import androidx.room.PrimaryKey 10 | 11 | @Entity 12 | class Movie() : BaseObservable() { 13 | constructor(name: String, actor: String, year: Int, score: Double) : this() { 14 | this.name = name 15 | this.actor = actor 16 | this.year = year 17 | this.score = score 18 | } 19 | 20 | @Ignore 21 | constructor(name: String, actor: String, year: Int) : this(name, actor, year, 8.0) { 22 | } 23 | 24 | @JvmField 25 | @PrimaryKey(autoGenerate = true) 26 | var id = 0 27 | 28 | @ColumnInfo(name = "movie_name", defaultValue = "Harry Potter") 29 | lateinit var name: String 30 | 31 | @ColumnInfo(name = "actor_name", defaultValue = "Jack Daniel") 32 | lateinit var actor: String 33 | 34 | @ColumnInfo(name = "post_year", defaultValue = "1999") 35 | var year = 1999 36 | 37 | @ColumnInfo(name = "review_score", defaultValue = "8.0") 38 | var score = 8.0 39 | 40 | fun getId(): Int { 41 | return id 42 | } 43 | 44 | fun setId(id: Int) { 45 | this.id = id 46 | } 47 | 48 | @JvmName("getName1") 49 | @Bindable 50 | fun getName(): String { 51 | return name 52 | } 53 | 54 | @JvmName("setName1") 55 | fun setName(name: String) { 56 | this.name = name 57 | notifyPropertyChanged(BR.name1) 58 | } 59 | 60 | @JvmName("getActor1") 61 | @Bindable 62 | fun getActor(): String { 63 | return actor 64 | } 65 | 66 | @JvmName("setActor1") 67 | fun setActor(actor: String) { 68 | this.actor = actor 69 | notifyPropertyChanged(BR.actor1) 70 | } 71 | 72 | @JvmName("getYear1") 73 | fun getYear(): Int { 74 | return year 75 | } 76 | 77 | @JvmName("setYear1") 78 | @Bindable 79 | fun setYear(year: Int) { 80 | this.year = year 81 | notifyPropertyChanged(BR.year1) 82 | } 83 | 84 | @JvmName("getScore1") 85 | fun getScore(): Double { 86 | return score 87 | } 88 | 89 | @JvmName("setScore1") 90 | @Bindable 91 | fun setScore(score: Double) { 92 | this.score = score 93 | notifyPropertyChanged(BR.score1) 94 | } 95 | 96 | override fun toString(): String { 97 | return "Movie{" + 98 | "id=" + id + 99 | ", name='" + name + '\'' + 100 | ", actor='" + actor + '\'' + 101 | ", year='" + year + '\'' + 102 | ", score='" + score + '\'' + 103 | '}' 104 | } 105 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/room/MovieDao.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.room 2 | 3 | import android.database.Cursor 4 | import androidx.lifecycle.LiveData 5 | import androidx.room.* 6 | 7 | @Dao 8 | interface MovieDao { 9 | @Insert 10 | fun insertWithOutId(movie: Movie?) 11 | 12 | @Insert 13 | fun insert(movie: Movie?): Long? 14 | 15 | @Insert 16 | fun insert(vararg movies: Movie?): LongArray? 17 | 18 | @Delete 19 | fun delete(movie: Movie?): Int 20 | 21 | @Delete 22 | fun delete(vararg movies: Movie?): Int 23 | 24 | @Update 25 | fun update(vararg movies: Movie?): Int 26 | 27 | @get:Query("SELECT * FROM movie") 28 | val allMovies: LiveData?> 29 | 30 | @get:Query("SELECT id, movie_name, actor_name, post_year, review_score FROM movie") 31 | val partMovies: LiveData?> 32 | 33 | @get:Query("SELECT * FROM movie ORDER BY post_year DESC") 34 | val allMoviesByOrder: LiveData?> 35 | 36 | @Query("SELECT * FROM movie WHERE id = :id") 37 | fun getMovie(id: Int): LiveData? 38 | 39 | @Query("SELECT * FROM movie WHERE post_year > :year") 40 | fun getMoveOverYear(year: Int): List? 41 | 42 | @Query("SELECT * FROM movie WHERE post_year BETWEEN :minYear AND :maxYear") 43 | fun getMoveOverYear(minYear: Int, maxYear: Int): LiveData?>? 44 | 45 | @Query("SELECT * FROM movie WHERE review_score >= :minScore") 46 | fun getMoveOverScore(minScore: Double): LiveData?>? 47 | 48 | @Query("SELECT * FROM movie WHERE movie_name LIKE :keyWord OR " + 49 | " actor_name LIKE :keyWord LIMIT 1") 50 | fun searchBestMove(keyWord: String?): Movie? 51 | 52 | @Query("SELECT * FROM movie WHERE movie_name LIKE :keyWord " + 53 | " OR actor_name LIKE :keyWord") 54 | fun searchMove(keyWord: String?): List? 55 | 56 | @Query("SELECT * FROM movie WHERE movie_name LIKE :keyWord " + 57 | " OR actor_name LIKE :keyWord") 58 | fun searchMoveFuzzyInternal(keyWord: String?): List? 59 | 60 | @Query("SELECT * FROM movie WHERE movie_name LIKE '%' || :keyWord || '%' " + 61 | " OR actor_name LIKE '%' || :keyWord || '%'") 62 | fun searchMoveFuzzy(keyWord: String?): List? 63 | 64 | @Query("SELECT * FROM movie WHERE movie_name LIKE '%' || :keyWord || '%' " + 65 | " OR actor_name LIKE '%' || :keyWord || '%' LIMIT :limit") 66 | fun searchMoveFuzzyByLimit(keyWord: String?, limit: Int): LiveData?>? 67 | 68 | @Query("SELECT * FROM movie WHERE movie_name IN (:keyWords)") 69 | fun findMove(keyWords: List?): List? 70 | 71 | @Query("SELECT * FROM movie WHERE movie_name LIKE '%' || :keyWord || '%' " + 72 | " OR actor_name LIKE '%' || :keyWord || '%' LIMIT :limit") 73 | fun searchMoveCursorByLimit(keyWord: String?, limit: Int): Cursor? 74 | 75 | @Query("SELECT * FROM movie WHERE movie_name LIKE '%' || :keyWord || '%' " + 76 | " OR actor_name LIKE '%' || :keyWord || '%'") 77 | fun searchMoveCursor(keyWord: String?): Cursor? 78 | 79 | @Transaction 80 | fun insetNewAndDeleteOld(newMovie: Movie?, oldMovie: Movie?) { 81 | insert(newMovie) 82 | delete(oldMovie) 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/room/MovieGestureCallback.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.room 2 | 3 | import android.util.Log 4 | import androidx.recyclerview.widget.ItemTouchHelper 5 | import androidx.recyclerview.widget.RecyclerView 6 | 7 | class MovieGestureCallback(private var listener: GestureListener?) 8 | : ItemTouchHelper.SimpleCallback(ItemTouchHelper.DOWN or ItemTouchHelper.UP, ItemTouchHelper.LEFT) { 9 | private val adapter: MovieAdapter? = null 10 | 11 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 12 | Log.d(MovieDataBase.TAG, "onSwiped() viewHolder:$viewHolder direction:$direction" 13 | + " listener:" + System.identityHashCode(listener) 14 | + " adapter:" + System.identityHashCode(adapter)) 15 | 16 | if (listener != null && direction == ItemTouchHelper.LEFT) { 17 | listener!!.onDeleteItem(viewHolder) 18 | } 19 | 20 | if (adapter != null && direction == ItemTouchHelper.LEFT) { 21 | adapter.onDeleteItem(viewHolder) 22 | } 23 | } 24 | 25 | override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, 26 | target: RecyclerView.ViewHolder): Boolean { 27 | Log.d(MovieDataBase.TAG, "onMove() recyclerView:$recyclerView viewHolder:$viewHolder target:$target") 28 | if (listener != null) { 29 | listener!!.onSwapItem(viewHolder, target) 30 | return true 31 | } 32 | return false 33 | } 34 | 35 | override fun onMoved(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, 36 | fromPos: Int, target: RecyclerView.ViewHolder, toPos: Int, x: Int, y: Int) { 37 | Log.d(MovieDataBase.TAG, "onMoved() recyclerView:$recyclerView") 38 | super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y) 39 | } 40 | 41 | override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { 42 | Log.d(MovieDataBase.TAG, "onSelectedChanged() actionState:$actionState") 43 | super.onSelectedChanged(viewHolder, actionState) 44 | if (listener != null && actionState == ItemTouchHelper.ACTION_STATE_IDLE) { 45 | listener!!.onGestureDone() 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/room/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.room 2 | 3 | import android.util.Log 4 | import java.util.* 5 | 6 | object Utils {// Create instances with id. 7 | // movies[i] = new Movie(i + 1, "Harry Potter" + i, "Daniel_" + i ); 8 | 9 | // // Create instances with same id. 10 | // movies[i] = new Movie(1, "Harry Potter" + i, "Daniel_" + i ); 11 | 12 | // Create instances without id. 13 | // movies[i] = new Movie("Harry Potter" + (i + 1), "Daniel_" + (i + 1), 1999 + i); 14 | // Test column null 15 | // movies[i] = new Movie("Harry Potter" + i, null); 16 | // movies[i] = new Movie("Harry Potter" + i); 17 | // public static Movie[] getInitData() { 18 | // List movies = new ArrayList<>(15); 19 | // for (int i = 0; i < 10; i++) { 20 | // movies.add(new Movie(i + 1, "Harry Potter" + i, "Daniel_" + i )); 21 | // } 22 | // return (Movie[]) movies.toArray(); 23 | // } 24 | val initData: Array 25 | get() { 26 | val movies = arrayOfNulls(9) 27 | for (i in movies.indices) { 28 | // Create instances with id. 29 | // movies[i] = new Movie(i + 1, "Harry Potter" + i, "Daniel_" + i ); 30 | 31 | // // Create instances with same id. 32 | // movies[i] = new Movie(1, "Harry Potter" + i, "Daniel_" + i ); 33 | 34 | // Create instances without id. 35 | // movies[i] = new Movie("Harry Potter" + (i + 1), "Daniel_" + (i + 1), 1999 + i); 36 | movies[i] = Movie("Harry Potter" + (i + 1), 37 | "Daniel_" + (i + 1), 38 | 1999 + i, 39 | (1 + i).toDouble()) 40 | // Test column null 41 | // movies[i] = new Movie("Harry Potter" + i, null); 42 | // movies[i] = new Movie("Harry Potter" + i); 43 | } 44 | Log.d(MovieDataBase.Companion.TAG, "getInitData movies:" + Arrays.toString(movies)) 45 | return movies 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewBinding/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewBinding; 2 | 3 | import android.os.Bundle; 4 | import android.widget.Toast; 5 | 6 | import com.ellison.jetpackdemo.R; 7 | import com.ellison.jetpackdemo.databinding.ActivityViewBindingBinding; 8 | 9 | import androidx.annotation.Nullable; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | 12 | public class DemoActivity extends AppCompatActivity { 13 | @Override 14 | protected void onCreate(@Nullable Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | 17 | ActivityViewBindingBinding binding = ActivityViewBindingBinding.inflate(getLayoutInflater()); 18 | setContentView(binding.getRoot()); 19 | 20 | binding.testLayout.setOnClickListener(view -> Toast.makeText(this, "U clicked empty area.", Toast.LENGTH_SHORT).show()); 21 | binding.testBtn.setOnClickListener(view -> Toast.makeText(this, "U clicked button.", Toast.LENGTH_SHORT).show()); 22 | getSupportFragmentManager().beginTransaction().replace(R.id.test_container, DemoFragment.newInstance()).commitNow(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewBinding/DemoFragment.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewBinding; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.Toast; 8 | 9 | import com.ellison.jetpackdemo.databinding.FragmentViewBindingBinding; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.annotation.Nullable; 13 | import androidx.fragment.app.Fragment; 14 | 15 | public class DemoFragment extends Fragment { 16 | FragmentViewBindingBinding mBinding; 17 | 18 | public static DemoFragment newInstance() { 19 | return new DemoFragment(); 20 | } 21 | 22 | @Nullable 23 | @Override 24 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 25 | mBinding = FragmentViewBindingBinding.inflate(inflater, container, false); 26 | return mBinding.getRoot(); 27 | } 28 | 29 | @Override 30 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 31 | mBinding.getRoot().setOnClickListener(view1 -> Toast.makeText(getContext(), "U clicked else empty area.", Toast.LENGTH_SHORT).show()); 32 | if (mBinding.testIv != null) { 33 | mBinding.testIv.setOnClickListener(view1 -> Toast.makeText(getContext(), "U clicked image.", Toast.LENGTH_SHORT).show()); 34 | } 35 | if (mBinding.testIvLand != null) { 36 | mBinding.testIvLand.setOnClickListener(view1 -> Toast.makeText(getContext(), "U clicked landscape image.", Toast.LENGTH_SHORT).show()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | 6 | import com.ellison.jetpackdemo.R; 7 | import com.ellison.jetpackdemo.databinding.ActivityViewModelBinding; 8 | 9 | import androidx.annotation.Nullable; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.appcompat.widget.SearchView; 12 | import androidx.lifecycle.HasDefaultViewModelProviderFactory; 13 | import androidx.lifecycle.ViewModelProvider; 14 | 15 | public class DemoActivity extends AppCompatActivity { 16 | @Override 17 | protected void onCreate(@Nullable Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | 20 | ActivityViewModelBinding binding = ActivityViewModelBinding.inflate(getLayoutInflater()); 21 | setContentView(binding.getRoot()); 22 | 23 | Log.d("ViewModel", "onCreate() GET VIEW MODEL INSTANCE WITH:" + ((HasDefaultViewModelProviderFactory) this).getDefaultViewModelProviderFactory()); 24 | // final PersonModel model = new ViewModelProvider(this).get(PersonModel.class); 25 | // final PersonModel model = ViewModelProviders.of(this).get(PersonModel.class); 26 | final PersonContextModel model = new ViewModelProvider(this).get(PersonContextModel.class); 27 | // final PersonContextModel model = new ViewModelProvider(this).get(PersonContextStateModel.class); 28 | 29 | Log.d("ViewModel", "onCreate() OBSERVE START"); 30 | model.mPersonLiveData.observe(this, person -> { 31 | Log.d("ViewModel", "onCreate() OBSERVED DATA:" + person); 32 | binding.testName.setText(person.getName()); 33 | binding.testAge.setText(String.valueOf(person.getAge())); 34 | } 35 | ); 36 | 37 | binding.testGet.setOnClickListener(view -> model.getPersonInWork()); 38 | binding.testUpdate.setOnClickListener(view -> model.updatePersonInWork()); 39 | binding.testFragment.setOnClickListener(view -> getSupportFragmentManager().beginTransaction().replace(R.id.test_container, new DemoFragment()).commit()); 40 | binding.testFragment2.setOnClickListener(view -> getSupportFragmentManager().beginTransaction().replace(R.id.test_container2, new OtherFragment()).commit()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/DemoFragment.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import com.ellison.jetpackdemo.databinding.FragmentViewModelBinding; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.annotation.Nullable; 13 | import androidx.fragment.app.Fragment; 14 | import androidx.lifecycle.ViewModelProvider; 15 | 16 | public class DemoFragment extends Fragment { 17 | private FragmentViewModelBinding fragmentViewModelBinding; 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 22 | Log.d("ViewModel", "onCreateView()"); 23 | fragmentViewModelBinding = FragmentViewModelBinding.inflate(inflater, container, false); 24 | return fragmentViewModelBinding.getRoot(); 25 | } 26 | 27 | @Override 28 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 29 | super.onViewCreated(view, savedInstanceState); 30 | new ViewModelProvider(getActivity()).get(PersonContextModel.class).mPersonLiveData.observe(this, person -> { 31 | // new ViewModelProvider(getActivity()).get(PersonContextStateModel.class).mPersonLiveData.observe(this, person -> { 32 | Log.d("ViewModel", "DemoFragment#onViewCreated() update"); 33 | fragmentViewModelBinding.name.setText(person.getName()); 34 | fragmentViewModelBinding.age.setText(String.valueOf(person.getAge())); 35 | }); 36 | 37 | PersonModel model = new ViewModelProvider(getActivity()).get(PersonModel.class); 38 | // PersonModel model = new ViewModelProvider(getActivity()).get(PersonStateModel.class); 39 | 40 | fragmentViewModelBinding.testGet.setOnClickListener(view2 -> { 41 | Log.d("ViewModel", "DemoFragment#onViewCreated() set value"); 42 | model.getPersonInWork(); 43 | }); 44 | 45 | fragmentViewModelBinding.testUpdate.setOnClickListener(view2 -> { 46 | Log.d("ViewModel", "DemoFragment#onViewCreated() set value"); 47 | model.updatePersonInWork(); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/OtherFragment.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import com.ellison.jetpackdemo.databinding.FragmentOtherViewModelBinding; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.annotation.Nullable; 13 | import androidx.fragment.app.Fragment; 14 | import androidx.lifecycle.ViewModelProvider; 15 | 16 | public class OtherFragment extends Fragment { 17 | private FragmentOtherViewModelBinding fragmentOtherViewModelBinding; 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 22 | Log.d("ViewModel", "onCreateView()"); 23 | fragmentOtherViewModelBinding = FragmentOtherViewModelBinding.inflate(inflater, container, false); 24 | return fragmentOtherViewModelBinding.getRoot(); 25 | } 26 | 27 | @Override 28 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 29 | super.onViewCreated(view, savedInstanceState); 30 | new ViewModelProvider(getActivity()).get(PersonModel.class).mPersonLiveData.observe(this, person -> { 31 | // new ViewModelProvider(getActivity()).get(PersonStateModel.class).mPersonLiveData.observe(this, person -> { 32 | Log.d("ViewModel", "OtherFragment#onViewCreated() update"); 33 | fragmentOtherViewModelBinding.name.setText(person.getName()); 34 | fragmentOtherViewModelBinding.age.setText(String.valueOf(person.getAge())); 35 | }); 36 | 37 | // new ViewModelProvider(this).get(PersonModel.class).mPersonLiveData.observe(this, person -> { 38 | // Log.d("ViewModel", "OtherFragment#onViewCreated() update person:" + person); 39 | // }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/Person.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | public class Person { 4 | private String name; 5 | private int age; 6 | 7 | public Person(int age, String name) { 8 | this.age = age; 9 | this.name = name; 10 | } 11 | 12 | public int getAge() { 13 | return age; 14 | } 15 | 16 | public void setAge(int age) { 17 | this.age = age; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public void setName(String name) { 25 | this.name = name; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "UserBean{" + 31 | "age=" + age + 32 | ", name='" + name + '\'' + 33 | '}'; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/PersonContextModel.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.lifecycle.AndroidViewModel; 8 | import androidx.lifecycle.MutableLiveData; 9 | 10 | public class PersonContextModel extends AndroidViewModel { 11 | public final MutableLiveData mPersonLiveData = new MutableLiveData<>(); 12 | 13 | public PersonContextModel(@NonNull Application application) { 14 | super(application); 15 | Log.d("ViewModel", getClass().getSimpleName() + "#PersonContextModel() WITH:" + getApplication(), new Throwable()); 16 | } 17 | 18 | public void getPersonInWork() { 19 | Log.d("ViewModel", getClass().getSimpleName() + "#getPersonInWork() WITH:" + getApplication()); 20 | Person testPerson = new Person(30, "ELC1020"); 21 | // Post set value task. 22 | mPersonLiveData.postValue(testPerson); 23 | } 24 | 25 | public void updatePersonInWork() { 26 | Log.d("ViewModel", getClass().getSimpleName() + "#updatePersonInWork() WITH:" + getApplication()); 27 | Person person = mPersonLiveData.getValue(); 28 | if (person != null) { 29 | // Set value directly. 30 | person.setName("Ellison"); 31 | person.setAge(20); 32 | mPersonLiveData.setValue(person); 33 | } 34 | } 35 | 36 | @Override 37 | protected void onCleared() { 38 | Log.d("ViewModel", getClass().getSimpleName() + "#onCleared() WITH:" + getApplication(), new Throwable()); 39 | super.onCleared(); 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/PersonContextStateModel.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.lifecycle.MutableLiveData; 8 | import androidx.lifecycle.SavedStateHandle; 9 | 10 | public class PersonContextStateModel extends PersonContextModel { 11 | public final MutableLiveData mPersonLiveData = new MutableLiveData<>(); 12 | 13 | public PersonContextStateModel(@NonNull Application application, SavedStateHandle handle) { 14 | super(application); 15 | Log.d("ViewModel", getClass().getSimpleName() + "#PersonContextStateModel() WITH:" + getApplication() + " handle:" + handle, new Throwable()); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/PersonModel.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.lifecycle.MutableLiveData; 6 | import androidx.lifecycle.ViewModel; 7 | 8 | public class PersonModel extends ViewModel { 9 | public final MutableLiveData mPersonLiveData = new MutableLiveData<>(); 10 | 11 | public PersonModel() { 12 | Log.d("ViewModel", getClass().getSimpleName() + "#PersonModel()", new Throwable()); 13 | } 14 | 15 | public PersonModel(String v) { 16 | Log.d("ViewModel", getClass().getSimpleName() + "#PersonModel()", new Throwable()); 17 | } 18 | 19 | public void getPersonInWork() { 20 | Log.d("ViewModel", getClass().getSimpleName() + "#getPersonInWork()"); 21 | Person testPerson = new Person(30, "ELC1020"); 22 | // Post set value task. 23 | mPersonLiveData.postValue(testPerson); 24 | } 25 | 26 | public void updatePersonInWork() { 27 | Log.d("ViewModel", getClass().getSimpleName() + "#updatePersonInWork()"); 28 | Person person = mPersonLiveData.getValue(); 29 | if (person != null) { 30 | // Set value directly. 31 | person.setName("Ellison"); 32 | person.setAge(20); 33 | mPersonLiveData.setValue(person); 34 | 35 | // // Can set value directly only at main thread, else exception occurred. 36 | // Executors.newSingleThreadExecutor().execute(() -> { 37 | // mPersonLiveData.setValue(person); 38 | // }); 39 | } 40 | } 41 | 42 | @Override 43 | protected void onCleared() { 44 | Log.d("ViewModel", getClass().getSimpleName() + "#onCleared()", new Throwable()); 45 | super.onCleared(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModel/PersonStateModel.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModel; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.lifecycle.MutableLiveData; 6 | import androidx.lifecycle.SavedStateHandle; 7 | 8 | public class PersonStateModel extends PersonModel { 9 | public final MutableLiveData mPersonLiveData = new MutableLiveData<>(); 10 | 11 | public PersonStateModel(SavedStateHandle handle) { 12 | Log.d("ViewModel", getClass().getSimpleName() + "#PersonStateModel()" + " handle:" + handle, new Throwable()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModelBinding/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModelBinding; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | 6 | import com.ellison.jetpackdemo.databinding.ActivityViewModelBindingBinding; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.lifecycle.ViewModelProvider; 11 | 12 | public class DemoActivity extends AppCompatActivity { 13 | PersonsContextModel model; 14 | ActivityViewModelBindingBinding binding; 15 | 16 | @Override 17 | protected void onCreate(@Nullable Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | 20 | binding = ActivityViewModelBindingBinding.inflate(getLayoutInflater()); 21 | setContentView(binding.getRoot()); 22 | 23 | model = new ViewModelProvider(this).get(PersonsContextModel.class); 24 | 25 | binding.setResponder(new EventResponder()); 26 | binding.setLifecycleOwner(this); 27 | } 28 | 29 | // Event binding. 30 | public class EventResponder { 31 | public void onClickGet() { 32 | Log.d("ViewModelBinding", "onClickGet() TITLE & GET INFO"); 33 | model.getPersonInWork(DemoActivity.this, persons -> { 34 | Log.d("ViewModelBinding", "observe GOT & NOTIFY"); 35 | binding.setDataList(persons); 36 | }); 37 | } 38 | 39 | public void onClickUpdate() { 40 | Log.d("ViewModelBinding", "onClickUpdate() TITLE & GET INFO"); 41 | model.updatePersonsInWork(DemoActivity.this, persons -> Log.d("ViewModelBinding", "observe UPDATED & NO NEED NOTIFY DUE TO DATA BINDING")); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModelBinding/Person.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModelBinding; 2 | 3 | import androidx.databinding.BaseObservable; 4 | import androidx.databinding.Bindable; 5 | import androidx.databinding.library.baseAdapters.BR; 6 | 7 | public class Person extends BaseObservable { 8 | private String name; 9 | private String age; 10 | private boolean adult; 11 | private boolean checked; 12 | 13 | public Person(String age, String name) { 14 | this.age = age; 15 | this.name = name; 16 | adult = this.age != null && Integer.valueOf(this.age) >= 18; 17 | } 18 | 19 | @Bindable 20 | public String getAge() { 21 | return age; 22 | } 23 | 24 | public void setAge(String age) { 25 | this.age = age; 26 | notifyPropertyChanged(BR.age); 27 | setAdult(); 28 | } 29 | 30 | @Bindable 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | notifyPropertyChanged(BR.name); 38 | } 39 | 40 | @Bindable 41 | public boolean isAdult() { 42 | return adult; 43 | } 44 | 45 | public void setAdult() { 46 | adult = this.age != null && Integer.valueOf(this.age) >= 18; 47 | notifyPropertyChanged(BR.adult); 48 | } 49 | 50 | @Bindable 51 | public boolean isChecked() { 52 | return checked; 53 | } 54 | 55 | public void setChecked(boolean checked) { 56 | this.checked = checked; 57 | notifyPropertyChanged(BR.checked); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "Person{" + 63 | "name='" + name + '\'' + 64 | ", age='" + age + '\'' + 65 | ", adult=" + adult + 66 | ", checked=" + checked + 67 | '}'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModelBinding/PersonsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModelBinding; 2 | 3 | import android.util.Log; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.ellison.jetpackdemo.databinding.ActivityViewModelBindingItemBinding; 8 | 9 | import java.util.List; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.databinding.BindingAdapter; 13 | import androidx.databinding.DataBindingUtil; 14 | import androidx.recyclerview.widget.DividerItemDecoration; 15 | import androidx.recyclerview.widget.LinearLayoutManager; 16 | import androidx.recyclerview.widget.RecyclerView; 17 | 18 | public class PersonsAdapter extends RecyclerView.Adapter { 19 | protected List personList; 20 | 21 | @BindingAdapter("bindData") 22 | public static void bindAdapter(RecyclerView recyclerView, List personList) { 23 | Log.d("ViewModelBinding", "PersonsAdapter#bindAdapter() recyclerView:" + recyclerView + " personList:" + personList, new Throwable()); 24 | recyclerView.setAdapter(new PersonsAdapter(personList)); 25 | recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext())); 26 | recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL)); 27 | } 28 | 29 | public PersonsAdapter(List personList) { 30 | Log.d("ViewModelBinding", "PersonsAdapter#PersonsAdapter()"); 31 | this.personList = personList; 32 | } 33 | 34 | @NonNull 35 | @Override 36 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 37 | Log.d("ViewModelBinding", "BindingAdapter#onCreateViewHolder() parent:" + parent); 38 | return new RecyclerView.ViewHolder(ActivityViewModelBindingItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false).getRoot()) {}; 39 | } 40 | 41 | @Override 42 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 43 | Log.d("ViewModelBinding", "BindingAdapter#onBindViewHolder() holder:" + holder + " position:" + position); 44 | ActivityViewModelBindingItemBinding binding = DataBindingUtil.bind(holder.itemView); 45 | binding.setPerson(personList.get(position)); 46 | // binding.executePendingBindings(); 47 | } 48 | 49 | @Override 50 | public int getItemCount() { 51 | return personList != null ? personList.size() : 0; 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ellison/jetpackdemo/viewModelBinding/PersonsContextModel.java: -------------------------------------------------------------------------------- 1 | package com.ellison.jetpackdemo.viewModelBinding; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.concurrent.Executors; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.lifecycle.AndroidViewModel; 12 | import androidx.lifecycle.LifecycleOwner; 13 | import androidx.lifecycle.MutableLiveData; 14 | import androidx.lifecycle.Observer; 15 | 16 | public class PersonsContextModel extends AndroidViewModel { 17 | public final MutableLiveData> personsLiveData; 18 | 19 | public PersonsContextModel(@NonNull Application application) { 20 | super(application); 21 | Log.d("ViewModelBinding", getClass().getSimpleName() + "#PersonContextModel() WITH:" + getApplication()); 22 | personsLiveData = new MutableLiveData<>(); 23 | } 24 | 25 | public void getPersonInWork(LifecycleOwner owner, Observer> observer) { 26 | Log.d("ViewModelBinding", getClass().getSimpleName() + "#getPersonInWork() WITH:" + getApplication()); 27 | personsLiveData.observe(owner, observer); 28 | 29 | Executors.newSingleThreadExecutor().execute(() -> { 30 | Log.d("ViewModelBinding", getClass().getSimpleName() + "#getPersonInWork() GOT ASYNC"); 31 | List persons = Arrays.asList(new Person[] { 32 | new Person("18", "Audi"), 33 | new Person("7", "Benz"), 34 | new Person("24", "Cadillac"), 35 | new Person("1", "DS"), 36 | new Person("34", "Ever"), 37 | new Person("199", "Ferrai")}); 38 | personsLiveData.postValue(persons); 39 | }); 40 | } 41 | 42 | public void updatePersonsInWork(LifecycleOwner owner, Observer> observer) { 43 | Log.d("ViewModelBinding", getClass().getSimpleName() + "#updatePersonInWork() WITH:" + getApplication()); 44 | List persons = personsLiveData.getValue(); 45 | personsLiveData.observe(owner, observer); 46 | 47 | Executors.newSingleThreadExecutor().execute(() -> { 48 | Log.d("ViewModelBinding", getClass().getSimpleName() + "#updatePersonInWork() UPDATE ASYNC"); 49 | if (persons != null) { 50 | persons.get(0).setAge("5"); 51 | persons.get(1).setAge("35"); 52 | persons.get(2).setAge("15"); 53 | persons.get(3).setAge("20"); 54 | persons.get(4).setAge("45"); 55 | persons.get(5).setAge("7"); 56 | } 57 | }); 58 | } 59 | 60 | @Override 61 | protected void onCleared() { 62 | Log.d("ViewModelBinding", getClass().getSimpleName() + "#onCleared() WITH:" + getApplication(), new Throwable()); 63 | super.onCleared(); 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/huawei_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/huawei_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/ic_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_analysis_picker_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 11 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera_change.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera_change_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera_new.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_record.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_record_disabled.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_record_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_record_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_record_pressing.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_record_pressing_center.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_capture_record_pressing_outter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/ic_filter.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_focus_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 11 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_jetpack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/ic_jetpack.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_movie_post.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/ic_movie_post.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_point_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_qr_code.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_qr_scan.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_qr_zone.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 11 | 12 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_rect_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/ic_sort.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_video.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ml_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/ml_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/zxing_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/zxing_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/zxing_logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/zxing_logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/zxing_logo_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ellisonchan/JetpackDemo/b8cda9f0a8e15977bd42a457a8b96c7df3df12d2/app/src/main/res/drawable/zxing_logo_transparent.png -------------------------------------------------------------------------------- /app/src/main/res/layout-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |