Universitas Kebudayaan Digital Makassar

Universitas Kebudayaan Digital Makassar (UKDMKS) memadukan inovasi teknologi digital dengan pelestarian budaya lokal. Bergabunglah untuk membangun masa depan berbasis budaya dan teknologi!

android jetpack android ktx android studio androidx

Belajar Aplikasi Android Studi Kasus Proyek Akademi : ViewModel dalam Proyek Academy

Tujuan

Pada Codelab kali ini Anda akan mempelajari bagaimana mengimplementasikan ViewModel dalam proyek Academy. Hasil dari codelab kali ini akan menjadi seperti ini:

20191218113703161173adba65c0c43802822c65f17d2f.gif

Logika Dasar

Membuka Aplikasi → memanggil DataDummy ke ViewModel → mengirim data ke Activity → melakukan perubahan rotasi → data masih terjaga.

Codelab ViewModel

  1. Bukalah proyek Academy yang sudah Anda buat sebelumnya atau Anda bisa unduh di sini.
  2. Lihatlah terlebih dahulu susunan package dan kelas yang sudah ada:
    20191218093730623be083ab749083b7d0476f6387c572.pngAnda akan membuat beberapa kelas ViewModel yang nantinya akan digunakan tiap Fragment atau Activity.
  3. Pertama, buka build.gradle level project dan tambahkan versi untuk library berikut:
    ext {
    ...
    archLifecycleVersion = '2.1.0'
    }

    Setelah itu, buka build.gradle level module: app dan tambahkan library berikut:

    dependencies {
    ...
    //architecture component
    implementation "androidx.lifecycle:lifecycle-viewmodel:$archLifecycleVersion"
    }
  4. Buatlah sebuah kelas baru di package academy dengan nama AcademyViewModel.
    7TmW-FNpmImK1rgSAvsCcbh42d6Jeh2AnLAcSZqtQn6LN0gJmWmJ9SN3JKWOLxGpfNd6_SGXRhR6xrtDIzlTMAcN5KKJrkuOPLmKNawTSYKCoiOE2vlyrars9l91aXA5Zjy4tbOq
    Kemudian pindahkan pemanggilan generateDummyCourse() dari AcademyFragment ke kelas AcademyViewModel:
    Kotlin
    class AcademyViewModel : ViewModel() {

    fun getCourses(): List<CourseEntity> = DataDummy.generateDummyCourses()
    }
    Java
    public class AcademyViewModel extends ViewModel {

    public List<CourseEntity> getCourses() {
    return DataDummy.generateDummyCourses();
    }

    }
  1. Kemudian hubungkan AcademyViewModel dengan AcademyFragment. Bukalah AcademyFragment dan tambahkan kode berikut:
    Kotlin
    class AcademyFragment : Fragment() {
    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    if (activity != null) {
    val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[AcademyViewModel::class.java]
    val courses = viewModel.getCourses()

    val academyAdapter = AcademyAdapter()
    academyAdapter.setCourses(courses)

    rv_academy.layoutManager = LinearLayoutManager(context)
    rv_academy.setHasFixedSize(true)
    rv_academy.adapter = academyAdapter
    }
    }
    }
    Java
    public class AcademyFragment extends Fragment {
    ...

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (getActivity() != null) {
    AcademyViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(AcademyViewModel.class);
    List<CourseEntity> courses = viewModel.getCourses();


    AcademyAdapter academyAdapter = new AcademyAdapter();
    academyAdapter.setCourses(courses);

    rvCourse.setLayoutManager(new LinearLayoutManager(getContext()));
    rvCourse.setHasFixedSize(true);
    rvCourse.setAdapter(academyAdapter);
    }
    }
    }
    Dengan begitu, sumber data sudah dipindahkan ke kelas ViewModel. Jalankan Aplikasi Academy, maka hasilnya akan seperti ini:
    201912180953227d294c14169842b541dbc1d651903a66.pngSecara tampilan tidak ada perubahan, karena Anda hanya memindahkan pengambilan data yang sebelumnya dari Activity menjadi ViewModel.
  1. Selanjutnya buatlah kelas ViewModel di package bookmark dan beri nama BookmarkViewModel.
    N4kBwz38aKAcvoAjNR78miJTBWMzhcjqWZLv-yoW8JnYqjTtvalMugC3VeLXoOuAQD5m_Ip4ZSPfjSlXPrA0Th8rIir42kZaUtpVeWRrLO-gBHocBGsAEorTH6OA6QUKbzkOX8TW
    Pindahkan pemanggilan generateDummyCourse() dari BookmarkFragment ke kelas BookmarkViewModel:
    Kotlin
    class BookmarkViewModel : ViewModel() {

    fun getBookmarks(): List<CourseEntity> = DataDummy.generateDummyCourses()
    }
    Java
    public class BookmarkViewModel extends ViewModel {
    List<CourseEntity> getBookmarks() {
    return DataDummy.generateDummyCourses();
    }

    }
  1. Kemudian hubungkan BookmarkAcademy dengan BookmarkFragment. Bukalah BookmarkAcademy dan tambahkan kode berikut:
    Kotlin
    class BookmarkFragment : Fragment(), BookmarkFragmentCallback {
    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    if (activity != null) {
    val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[BookmarkViewModel::class.java]
    val courses = viewModel.getBookmarks()


    val adapter = BookmarkAdapter(this)
    adapter.setCourses(courses)

    rv_bookmark.layoutManager = LinearLayoutManager(context)
    rv_bookmark.setHasFixedSize(true)
    rv_bookmark.adapter = adapter
    }
    }

    ...
    }
    Java
    public class BookmarkFragment extends Fragment implements BookmarkFragmentCallback {
    ...

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (getActivity() != null) {
    BookmarkViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(BookmarkViewModel.class);
    List<CourseEntity> courses = viewModel.getBookmarks();


    BookmarkAdapter adapter = new BookmarkAdapter(this);
    adapter.setCourses(courses);

    rvBookmark.setLayoutManager(new LinearLayoutManager(getContext()));
    rvBookmark.setHasFixedSize(true);
    rvBookmark.setAdapter(adapter);
    }
    }

    ...
    }
    Dengan begitu, sumber data sudah dipindahkan ke kelas ViewModel.
  1. Selanjutnya buatlah kelas ViewModel untuk DetailCourseActivity di package detail dan beri nama DetailCourseViewModel.
    t3ub-LoQc-zsvlVnoHAajLLZKzd5SEMzgCs3QiO6INIQWabdvvLL-tOE3XRO4x1fUu5PUzgme3Hn-mpeERSAy53ut7VFhC7iTC3a9ZLIXXA1U_O764nicWh1KQkqLMIye3fV0l9d
    Tambahkan kode pada kelas tersebut untuk menetapkan atau mendapatkan courseId, mendapatkan list module dan mendapatkan CourseEntity.
    Kotlin
    class DetailCourseViewModel : ViewModel() {
    private lateinit var courseId: String

    fun setSelectedCourse(courseId: String) {
    this.courseId = courseId
    }

    fun getCourse(): CourseEntity {
    lateinit var course: CourseEntity
    val coursesEntities = DataDummy.generateDummyCourses()
    for (courseEntity in coursesEntities) {
    if (courseEntity.courseId == courseId) {
    course = courseEntity
    }
    }
    return course
    }

    fun getModules(): List<ModuleEntity> = DataDummy.generateDummyModules(courseId)
    }
    Java
    public class DetailCourseViewModel extends ViewModel {
    private String courseId;

    public void setSelectedCourse(String courseId) {
    this.courseId = courseId;
    }

    public CourseEntity getCourse() {
    CourseEntity course = null;
    ArrayList<CourseEntity> courseEntities = DataDummy.generateDummyCourses();
    for (CourseEntity courseEntity : courseEntities) {
    if (courseEntity.getCourseId().equals(courseId)) {
    course = courseEntity;
    }
    }
    return course;
    }

    public List<ModuleEntity> getModules() {
    return DataDummy.generateDummyModules(courseId);
    }
    }
  1. Selanjutnya ubahlah kode yang ada di DetailCourseActivity untuk menghubungkan DetailCourseViewModel.
    Kotlin
    class DetailCourseActivity : AppCompatActivity() {
    ...

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_detail_course)
    ...

    val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[DetailCourseViewModel::class.java]

    val extras = intent.extras
    if (extras != null) {
    val courseId = extras.getString(EXTRA_COURSE)
    if (courseId != null) {
    viewModel.setSelectedCourse(courseId)
    val modules = viewModel.getModules()
    adapter.setModules(modules)
    populateCourse(viewModel.getCourse())
    }
    }

    ...
    }

    ...
    }
    Java
    public class DetailCourseActivity extends AppCompatActivity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_detail_course);
    ...

    DetailCourseViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(DetailCourseViewModel.class);

    Bundle extras = getIntent().getExtras();
    if (extras != null) {
    String courseId = extras.getString(EXTRA_COURSE);
    if (courseId != null) {
    viewModel.setSelectedCourse(courseId);
    List<ModuleEntity> modules = viewModel.getModules();
    adapter.setModules(modules);
    populateCourse(viewModel.getCourse());

    }
    }

    ...
    }

    ...
    }
    Dengan bantuan kelas ViewModel, courseId akan dipertahankan sampai Activity masuk ke state onDestroy.
  1. Buatlah kembali kelas ViewModel yang akan digunakan untuk CourseReaderActivity. Kemudian beri nama CourseReaderViewModel.
    Nf7nR4S4nZqKu5FkaQ1fedHKVDyRUVXQ41KTHG52Iavdw-mNQuVmB8UXTZUPrT756r6L6irqlasINvD-atWg_2XB-bdiesRLWMhSb7AUr8IYrT8fpX4lAsaTh6Vbs8wzv-vNKC9C
    Setelah itu tambahkanlah kode di kelas tersebut:
    Kotlin
    class CourseReaderViewModel : ViewModel() {

    private lateinit var courseId: String
    private lateinit var moduleId: String

    fun setSelectedCourse(courseId: String) {
    this.courseId = courseId
    }

    fun setSelectedModule(moduleId: String) {
    this.moduleId = moduleId
    }

    fun getModules(): ArrayList<ModuleEntity> = DataDummy.generateDummyModules(courseId)

    fun getSelectedModule(): ModuleEntity {
    lateinit var module: ModuleEntity
    val moduleEntities = getModules()
    for (moduleEntity in moduleEntities) {
    if (moduleEntity.moduleId == moduleId) {
    module = moduleEntity
    module.contentEntity = ContentEntity("<h3 class=\"fr-text-bordered\">" + module.title + "</h3><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>")
    break
    }
    }
    return module
    }
    }
    Java
    public class CourseReaderViewModel extends ViewModel {

    private String courseId;
    private String moduleId;

    public void setSelectedCourse(String courseId) {
    this.courseId = courseId;
    }

    public void setSelectedModule(String moduleId) {
    this.moduleId = moduleId;
    }

    public ArrayList<ModuleEntity> getModules() {
    return DataDummy.generateDummyModules(courseId);
    }

    public ModuleEntity getSelectedModule() {
    ModuleEntity module = null;
    ArrayList<ModuleEntity> moduleEntities = getModules();
    for (ModuleEntity moduleEntity: moduleEntities) {
    if (moduleEntity.getModuleId().equals(moduleId)) {
    module = moduleEntity;
    module.contentEntity = new ContentEntity("<h3 class=\"fr-text-bordered\">" + module.getTitle() + "</h3><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>");
    break;
    }
    }
    return module;
    }
    }

    CourseReaderViewModel nantinya juga akan digunakan di ModuleContentFragment dan ModuleListFragment.

  1. Setelah membuat kelas ViewModel, bukalah CourseReaderActivity dan sesuaikanlah menjadi seperti ini:
    Kotlin
    class CourseReaderActivity : AppCompatActivity(), CourseReaderCallback {

    ...

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_course_reader)
    val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[CourseReaderViewModel::class.java]

    val bundle = intent.extras
    if (bundle != null) {
    val courseId = bundle.getString(EXTRA_COURSE_ID)
    if (courseId != null) {
    viewModel.setSelectedCourse(courseId)
    populateFragment()
    }
    }
    }
    }
    Java
    public class CourseReaderActivity extends AppCompatActivity implements CourseReaderCallback {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_course_reader);

    CourseReaderViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(CourseReaderViewModel.class);

    Bundle bundle = getIntent().getExtras();
    if (bundle != null) {
    String courseId = bundle.getString(EXTRA_COURSE_ID);
    if (courseId != null) {
    viewModel.setSelectedCourse(courseId);
    populateFragment();
    }
    }
    }

    ...
    }
  1. Selanjutnya bukalah ModuleListFragment, dan ubah menjadi seperti ini:
    Kotlin
    class ModuleListFragment : Fragment(), MyAdapterClickListener {
    ...

    private lateinit var viewModel: CourseReaderViewModel

    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.NewInstanceFactory())[CourseReaderViewModel::class.java]
    adapter = ModuleListAdapter(this)
    populateRecyclerView(viewModel.getModules())

    }

    ...

    override fun onItemClicked(position: Int, moduleId: String) {
    courseReaderCallback.moveTo(position, moduleId)
    viewModel.setSelectedModule(moduleId)
    }

    ...
    }
    Java
    public class ModuleListFragment extends Fragment implements MyAdapterClickListener {

    ...

    private CourseReaderViewModel viewModel;

    ...

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (getActivity() != null) {
    viewModel = new ViewModelProvider(requireActivity(), new ViewModelProvider.NewInstanceFactory()).get(CourseReaderViewModel.class);
    adapter = new ModuleListAdapter(this);
    populateRecyclerView(viewModel.getModules());
    }

    }

    ...

    @Override
    public void onItemClicked(int position, String moduleId) {
    courseReaderCallback.moveTo(position, moduleId);
    viewModel.setSelectedModule(moduleId);
    }
    }
    Dengan menambahkan ViewModel di atas, maka sumber data sudah dipindahkan ke ViewModel.
  1. Bukalah ModuleContentFragment dan sesuaikanlah kode pada kelas tersebut menjadi seperti ini:
    Kotlin
    class ModuleContentFragment : Fragment() {

    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    if (activity != null) {
    val viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.NewInstanceFactory())[CourseReaderViewModel::class.java]
    val module = viewModel.getSelectedModule()

    populateWebView(module)
    }
    }

    private fun populateWebView(module: ModuleEntity) {
    web_view.loadData(module.contentEntity?.content, "text/html", "UTF-8")
    }
    }
    Java
    public class ModuleContentFragment extends Fragment {
    ...

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (getActivity() != null) {
    CourseReaderViewModel viewModel = new ViewModelProvider(requireActivity(), new ViewModelProvider.NewInstanceFactory()).get(CourseReaderViewModel.class);
    ModuleEntity module = viewModel.getSelectedModule();
    populateWebView(module);
    }
    }

    private void populateWebView(ModuleEntity module) {
    webView.loadData(module.contentEntity.getContent(), "text/html", "UTF-8");
    }
    }
    Mengapa ModuleContentFragment bisa langsung tahu ModuleEntity? Jika Anda lihat, tidak ada masukan courseId dan moduleId. Hal ini bisa terjadi karena courseId sudah dimasukkan di CourseReaderActivity dan moduleId dimasukkan di ModuleListFragment. Inilah yang disebut share ViewModel, membagikan ViewModel ke kelas lain. Jadi perlu diperhatikan, pemanggil ViewModel dengan Fragment tersebut:
    Kotlin
    viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.NewInstanceFactory())[CourseReaderViewModel::class.java]
    Java
    viewModel = new ViewModelProvider(requireActivity(), new ViewModelProvider.NewInstanceFactory()).get(CourseReaderViewModel.class);
    Catatan:Jika Anda ganti requireActivity() dengan this, maka Fragment tidak akan mengambil ViewModel dari Activity tetapi akan membuat ViewModel baru.
  1. Langkah terakhir adalah menjalankan aplikasi Anda dan tampilannya akan jadi seperti ini:2019121811345450fe29586c4ebef713ec4a0a075497f6.gifJika dilihat, tidak ada perbedaan tampilan awal karena Anda hanya memindahkan data yang awalnya di Activity menjadi ViewModel. Yang berbeda yaitu tampilan di tiap modul sekarang berbeda-beda sesuai dengan modul yang dipilih, tidak seperti sebelumnya yang sama semua.

Bedah Kode

ViewModel

Perhatikan pemanggilan ViewModel berikut:
AcademyFragment
Kotlin
val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[AcademyViewModel::class.java]
Java
AcademyViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(AcademyViewModel.class);
DetailCourseActivity
Kotlin
val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[DetailCourseViewModel::class.java]
Java
DetailCourseViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(DetailCourseViewModel.class);
Untuk pemanggilan ViewModel antara Activity dengan Fragment itu sama. Yang membedakan ketika Fragment akan menggunakan ViewModel yang ada pada Activity (shared ViewModel). Contohnya adalah seperti ini:
ModelContentFragment
Kotlin
val viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.NewInstanceFactory())[CourseReaderViewModel::class.java]
Java
CourseReaderViewModel viewModel = new ViewModelProvider(requireActivity(), new ViewModelProvider.NewInstanceFactory()).get(CourseReaderViewModel.class);
Jadi this diganti dengan requireActivity() untuk menghubungkan Fragment dengan ViewModel yang dipakai di Activity.

Anda bisa unduh proyek Academy tentang ViewModel di sini:
Codelabs selanjutnya akan membahas tentang unit testing dan instrumental testing yang ada pada proyek Academy. Tetap semangat!
Asalas | Unlock Anime, Manhwa & Donghua Like Never BeforeFinasteriden: Unlock the Secrets of the Movie World!Marians Woman: Celebrating Beauty Around the WorldArheon - Unveiling the Secrets of Food & Origins WorldwideMPAPER The Ultimate Destination for Anime & Manga FansANMC21: Your Guide to Smarter Living and SuccessMV Agusta of Tampa: Your Automotive News HubTech and Play: Your Hub for Technology, Gaming, and GadgetsRebeccaSommer : Stories & Cultures from Around the WorldUETRABAJANDOJUNTOS - Inside the Music World’s Best-Kept SecretsVandelay Armor - Viral News and Global UpdatesGainesville Sun: Technology & Computers UnveiledGRFX Gaming Party Bus: Journey Through Gaming ErasHouse of Beauty: Celebrating the World's Most Beautiful WomenLearn Mistake: Wisdom for a Better LifeSports Hope Alive: Portal Olahraga DuniaWorld News TW - The Hottest Viral News & Global HeadlinesWriter Sujatha - Life Lessons & Struggles That Inspirehttps://128.199.185.186/https://143.198.89.74/https://165.227.47.178/https://170.64.208.214/https://170.64.192.239/https://46.101.102.216/LVONLINEtelegram lvonlinehttps://www.thecarbongenie.com/https://www.aievea-bijou.com/https://www.slashpolicy.com/https://www.benwestbeech.com/https://www.hh-bags.com/https://www.drupalforfacebook.org/https://www.lvonline.boats/https://www.lvoslot.com/https://www.lvobet.id/https://www.lvoslot.id/https://www.lvonline000.com/https://www.lvonline001.com/https://www.lvonline002.com/https://www.lvonline003.com/https://www.lvonline004.com/https://www.lvonline005.com/https://www.lvonline006.com/https://www.lvonline007.com/https://www.lvonline008.com/https://www.lvonline009.com/https://www.lvonline010.com/https://www.lvonlinepoker.com/https://www.lvonlinebola.com/https://www.lvonlinekasino.com/https://www.lvonline.io/https://www.lvonline.store/https://www.lvonline.online/https://www.situslvonline.us/situs bandar lvonlinehttps://146.190.97.83/https://143.198.209.134/https://188.166.246.204/https://167.172.83.179/https://togelhok.tv/https://www.togelhok.id/https://earthtoweb.com/https://www.elearningfacultymodules.org/https://www.how6youtoknowc.org/https://128.199.71.129/https://167.172.64.185/https://152.42.192.250/https://www.capcut88.com/https://www.capcut88.co/https://towsonsmiles.com/https://www.campur88.com/https://www.campur88.org/https://www.campur88.work/https://www.campur88.xyz/https://www.campur88.lol/https://www.nagacampur.biz/https://www.nagacampur.club/https://www.nagacampur.co/https://www.nagacampur.me/https://www.nagacampur.xyz/https://www.nasicampur88.com/https://165.232.175.185/https://152.42.164.228/https://152.42.194.203/https://152.42.169.214/https://www.campurslot.com/https://www.campurslot.id/https://www.campurslot.co/https://www.campurslot.org/https://www.campurslot.homes/https://www.campurslot.design/Badan Lembaga Pendidikan Ilmu Komputer SubangUniversitas Negeri JeparaLembaga Universitas Kristen MaranthaSMP 3 Negri Nganjukhttps://iklanmalay.com/https://promobola.comhttps://kapsychologists.comhttps://propertycloudsite.comSattar777 NewsVR Slot Online NewsRuby888 Online Slot NewsBerita Global Klik Agenslot228Agen Sloto SG777 NewsGold Club SlotOnline NewsAzar Marra Kech NewsBerita Sidney dan MancanegaraBerita Seputar Sumsel dan DuniaBerita Rehabilitasi Judi OnlineBerita Link Gacor TerupdateItalian Tuition News UpdateWPJS Online NewsBerita Agen Slot RoyalThai Slot Online NewsAll Online Game NewsOnline Game News UpdateAsian Lotre NewsBerita Demo Dana SlotBerita Kalbar ProvLocanda della Maria NewsWye Valley NewsBerita Agen Slot1004Berita Agen Slot33Agen Slot399 NewsPlayboy Slot Online NewsSlot Online BB Slot NewsSlot Online 911 News365 Slot Online NewsEat Atlah Newsambamali canadaInfo Seputar Sepakbolacentre thoughtBerita Hiburanopen etherpadras indo groupresistance manualPrediksi Shiowe want real newsthe poisoned pawnindonesia reclaimed teakswift kennedy and copullip passionmy passion foraim torontoPublic FlashesFriweb TeknologiIngenious Gamersthe late show gardensGishPuppy Newsslot danaOregon Farm Garden NewsViral Pulse GlobaljumpajpPromo Bola soccer Captivates The WorldKapsychologists World First The Science of Mental Health - Understanding Psychiatry: The Science of Mental HealthPropertyCloudSite How to Make Smarter Investments in Today’s MarketArnavichara Ultimate Guide Right Business SoftwareAuscare Disability A Comprehensive Guide to Retirement Homes Finding the Perfect Place to Enjoy Your Golden YearsSeries Mp4 The Future of Entertainment Streaming and Downloadable Video ExplainedAlogirlxinh How to Create a Successful Personal Page or Blog in 2024ihokibethttps://bengbulang-karangpucung.cilacapkab.go.id/https://comunicacion.unsa.edu.ar/https://seychellesbiodiversitychm.sc/https://www.925lms.com/https://www.guisseny.memoire.bzh/https://www.mobiliars.org/https://www.squashparkwieliczka.pl/
Asalas | Unlock Anime, Manhwa & Donghua Like Never BeforeFinasteriden: Unlock the Secrets of the Movie World!Marians Woman: Celebrating Beauty Around the WorldArheon - Unveiling the Secrets of Food & Origins WorldwideMPAPER The Ultimate Destination for Anime & Manga FansANMC21: Your Guide to Smarter Living and SuccessMV Agusta of Tampa: Your Automotive News HubTech and Play: Your Hub for Technology, Gaming, and GadgetsRebeccaSommer : Stories & Cultures from Around the WorldUETRABAJANDOJUNTOS - Inside the Music World’s Best-Kept SecretsVandelay Armor - Viral News and Global UpdatesGainesville Sun: Technology & Computers UnveiledGRFX Gaming Party Bus: Journey Through Gaming ErasHouse of Beauty: Celebrating the World's Most Beautiful WomenLearn Mistake: Wisdom for a Better LifeSports Hope Alive: Portal Olahraga DuniaWorld News TW - The Hottest Viral News & Global HeadlinesWriter Sujatha - Life Lessons & Struggles That Inspirehttps://128.199.185.186/https://143.198.89.74/https://165.227.47.178/https://170.64.208.214/https://170.64.192.239/https://46.101.102.216/LVONLINEtelegram lvonlinehttps://www.thecarbongenie.com/https://www.aievea-bijou.com/https://www.slashpolicy.com/https://www.benwestbeech.com/https://www.hh-bags.com/https://www.lvonline.boats/https://www.lvoslot.com/https://www.lvobet.id/https://www.lvoslot.id/https://www.lvonline000.com/https://www.lvonline001.com/https://www.lvonline002.com/https://www.lvonline003.com/https://www.lvonline004.com/https://www.lvonline005.com/https://www.lvonline006.com/https://www.lvonline007.com/https://www.lvonline008.com/https://www.lvonline009.com/https://www.lvonline010.com/https://www.lvonlinepoker.com/https://www.lvonlinebola.com/https://www.lvonlinekasino.com/https://www.lvonline.io/https://www.lvonline.store/https://www.lvonline.online/https://www.situslvonline.us/situs bandar lvonlinehttps://146.190.97.83/https://143.198.209.134/https://188.166.246.204/https://167.172.83.179/https://togelhok.tv/https://www.togelhok.id/https://earthtoweb.com/https://www.elearningfacultymodules.org/https://www.how6youtoknowc.org/https://128.199.71.129/https://167.172.64.185/https://152.42.192.250/https://www.capcut88.com/https://www.capcut88.co/https://towsonsmiles.com/https://www.campur88.com/https://www.campur88.org/https://www.campur88.work/https://www.campur88.xyz/https://www.campur88.lol/https://www.nagacampur.biz/https://www.nagacampur.club/https://www.nagacampur.co/https://www.nagacampur.me/https://www.nagacampur.xyz/https://www.nasicampur88.com/https://165.232.175.185/https://152.42.164.228/https://152.42.194.203/https://152.42.169.214/https://www.campurslot.com/https://www.campurslot.id/https://www.campurslot.co/https://www.campurslot.org/https://www.campurslot.homes/https://www.campurslot.design/Badan Lembaga Pendidikan Ilmu Komputer SubangUniversitas Negeri JeparaLembaga Universitas Kristen MaranthaSMP 3 Negri Nganjukhttps://iklanmalay.com/https://promobola.comhttps://kapsychologists.comhttps://propertycloudsite.comSattar777 NewsVR Slot Online NewsRuby888 Online Slot NewsBerita Global Klik Agenslot228Agen Sloto SG777 NewsGold Club SlotOnline NewsAzar Marra Kech NewsBerita Sidney dan MancanegaraBerita Seputar Sumsel dan DuniaBerita Rehabilitasi Judi OnlineBerita Link Gacor TerupdateItalian Tuition News UpdateWPJS Online NewsBerita Agen Slot RoyalThai Slot Online NewsAll Online Game NewsOnline Game News UpdateAsian Lotre NewsBerita Demo Dana SlotBerita Kalbar ProvLocanda della Maria NewsWye Valley NewsBerita Agen Slot1004Berita Agen Slot33Agen Slot399 NewsPlayboy Slot Online NewsSlot Online BB Slot NewsSlot Online 911 News365 Slot Online NewsEat Atlah Newsambamali canadaInfo Seputar Sepakbolacentre thoughtBerita Hiburanopen etherpadras indo groupresistance manualPrediksi Shiowe want real newsthe poisoned pawnindonesia reclaimed teakswift kennedy and copullip passionmy passion foraim torontoPublic FlashesFriweb TeknologiIngenious Gamersthe late show gardensGishPuppy Newsslot danaOregon Farm Garden NewsViral Pulse GlobaljumpajpPromo Bola soccer Captivates The WorldKapsychologists World First The Science of Mental Health - Understanding Psychiatry: The Science of Mental HealthPropertyCloudSite How to Make Smarter Investments in Today’s MarketArnavichara Ultimate Guide Right Business SoftwareAuscare Disability A Comprehensive Guide to Retirement Homes Finding the Perfect Place to Enjoy Your Golden YearsSeries Mp4 The Future of Entertainment Streaming and Downloadable Video ExplainedAlogirlxinh How to Create a Successful Personal Page or Blog in 2024ihokibethttps://bengbulang-karangpucung.cilacapkab.go.id/https://comunicacion.unsa.edu.ar/https://seychellesbiodiversitychm.sc/https://www.925lms.com/https://www.guisseny.memoire.bzh/https://www.mobiliars.org/https://www.squashparkwieliczka.pl/https://mok.edu.kz/https://www.mware.cloud/https://www.facilitiescoolingandheating.com.au/https://www.krillpay.ng/https://925worksuite.com/