How to Build Your Own Wallpaper Setting App - NullClass

Special Sale the courses from 497 rs for limited time.

How to Build Your Own Wallpaper Setting App

How to Build Your Own Wallpaper Setting App

Wallpaper4kClone

 

  • Initial setup ->
  • First we add the necessary dependencies in build.gradle (Project) ->
  • Next in the build.gradle(Module) we add the necessary plugins (add them in same order. The plugins need to be applied in a specific order)
  • Next, we enable viewbinding by adding the buildFeatures block ->
  • Now we add the necessary dependencies-
  • Importing the resources->
  • Go to res-> drawbles. Right click and go to new -> vector asset.
  • In the dialog that appears, click on clip art and type “home” in the search bar.
  • Name it “home_icon” and click on next-> finish.
  • Similarly, create the following vector assets-> (Give them the same name as mentioned)
  • toys_icon ->
  • download_icon ->
  • phone_icon->
  • search_icon->
  • lock_icon->
  • share_icon->
  • Next we create the menu resource file for our search bar in the toolbar.
  • Right click on res, go to new -> Android Resource file
  • Set the resource type to menu and give it a name “search_menu”
  • Add the following code to the search_menu.xml->
  • Creating the model classes->
  • In the root package, create a new package called models.
  • In the models package, create a new data class “WallpaperResponse” and add the following code ->
  • This will serve as the model class for the REST API response we get.
  • The serialized name annotation defines the name of the field (in our case , an arraylist of wallpapers) in the json response we get.
  • Retrofit setup –
  • Now we will setup our networking code.
  • Create a new package in the root directory like we did before and call it api.
  • Create a new interface and name it “PixaBayAPI”

Now, add the following code to the interface file –

 

interface PixaBayAPI {
companion object {
const val BASE_URL = “https://pixabay.com/”
const val KEY = “19099131-67b916bd4453d12d03a19ae99”
}

@GET(“api/”)
suspend fun getWallpapers(
@Query(“key”) key: String = KEY,
@Query(“q”) query: String,
@Query(“orientation”) orientation: String = “vertical”
): WallpaperResponse

  • The BASE_URL is the url of the pixabay api which will be used to generate retrofit instance. The “KEY” is the api key that you will obtain from the pixabay website.
  • We make a GET request from the getWallpapers function. Since the network can take a few seconds to respond, we make this function a suspending function by using the “suspend” keyword.
  • This allows Android OS to execute the function on a background thread and “suspend” its execution if needed, (say, in case there is heavy load on the UI thread)

 

  • Dependency Injection –
  • Dependency injection is a way to provide a class with all the objects (dependencies) it needs to operate without the class actually creating those objects on its own. This makes the code cleaner, and more testable.
  • We will use dagger-Hilt library of google’s Android jetpack collection for dependency injection
  • Create a new package in the root directory and call it “di”
  • Create a new object and call it “AppModule”

 

Add the following code to the AppModule –

  • The “@Module”  tells dagger – hilt that this object provides dependencies. The @Installin(SingletonComponent::class) makes the dependencies described in this module live as long as the application keeps running. This allows us to use these dependencies anywhere in our app.
  • The “@Provides” annotation tells dagger-hilt that this function provides a dependency (object). The @Singleton annotation makes the object to be generated only once and then later, the same object is injected where ever it is required. This makes the code more efficient, since now, we won’t be creating new objects again and again.

@Provides
@Singleton
fun providesRetrofit(): Retrofit = Retrofit.Builder()
.baseUrl(PixaBayAPI.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()

  • We use Kotlin’s short method syntax (instead of using curly braces and a return statement, we just use an “=” sign).
  • This makes our code cleaner.
  • The GsonConvertor class allows us to convert the json object we get from REST API into a kotlin object.

 

Now we create our API instance –

@Provides
@Singleton
fun providesPixaBayAPI(retrofit: Retrofit): PixaBayAPI =
retrofit.create(PixaBayAPI::class.java)

  • Dagger hilt automatically takes the retrofit instance from the “providesRetrofit” function we created earlier and passes it to the “providedPixaBayAPI” function as an argument.

 

  • Creating utility classes –

Create a new package in the root directory and call it utils.

Create a new sealed class and name it Resource –

 

Add the following code to the resource class –

 

sealed class Resource<T>(
val data: T? = null,
val error: Throwable? = null
) {
class Success<T>(data: T) : Resource<T>(data)
class Loading<T>(data: T? = null) : Resource<T>(data)
class Error<T>(throwable: Throwable, data: T? = null) : Resource<T>(data, throwable)
}

  • The Resource class acts as a wrapper around the data we get from the rest API.
  • Loading class represents the data is still being  loaded, success means the data has been retrieved, and error means some error occurred in the network request.
  • All these three classes inherit from the Resources Sealed class.
  • We give the Resource class a generic type argument “T”. This makes the class re-usable with any type of network data.

 

  • Setting up the ViewModel ->
  • Create a new package in root directory and name it viewModel.
  • Create a new class in this package and name it “WallpaperViewModel”

Add the following code –

Lots of code, so we will take it step by step.

 

  • Firstly, the @HiltViewModel” annotation tells dagger-hilt that the WallPaperViewModel is a viewModel class.
  • The @Inject annotation allows dagger-hilt to inject the pixaBayAPI automatically at runtime (this is the power of dependency injection. In larger projects with a few 10’s of dependencies, dagger hilt becomes very useful)

 

 

  • The “query” parameter is a MutableStateFlow value which stores the current query keyword (by default it is set to cats).
  • Whenever a search is executed, we call the searchWallpapers function which sets the query value to the passed String parametres.
  • Whenever the value of the MutableStateFlow “query” is changed, it triggers the flatMapLatest operator , and the getWallPapers function gets called with the given query.
  • This is the benefit of using a reactive data structure like MutableStateFlow. The code gets executed automatically whenever the value of “query” changes.
  • The “stateIn” function stores the data in the viewModelScope, which means it will be stored as long as the viewModel is in memory.
  • Now, lets look at the getWallpapers function. It takes a query as a parameter and return a flow of Wallpaper wrapped in the Resource generic we created earlier (we will soon see why we need this wrapper)
  • The “currentPosition” is a mutable live data value (live data whose value can be changed).
  • It stores the current position of the wallpaper list.
  • Similarly, the “currentWallpaperCount” stores the count of wallpapers in the list.

 

  • We first set the currentPosition to 1 and emit a Loading Response from the function (the data is being loaded)
  • Now, we make the network request. The pixaBayAPI is injected automatically in the constructor of the viewModel by dagger-Hilt.
  •   We make the request in a try catch block and once the request is successful, we emit a      “Resource.Success(wallpaperResponse)” as a result.  In the catch block, if anything went wrong with the api request (say due to bad internet        connection) we emit a Resource.Error and pass the exception in the constructor.   The isOpen is another MutableStateFlow that stores whether the floating action menu in the UI is opened or not (we will come to the UI later) The currentWallpaper variable stores the wallpaper we are currently dealing with (again, this will be used in the UI)  In the end, the viewModel looks like this –  @HiltViewModel
    class WallpaperViewModel @Inject constructor(private val pixaBayAPI: PixaBayAPI) : ViewModel() {
    private val query = MutableStateFlow(“cats”)
    @ExperimentalCoroutinesApi
    val wallpapers = query.flatMapLatest { query ->
    getWallpapers(query)
    }.stateIn(viewModelScope, SharingStarted.Lazily, null)fun searchWallPapers(s: String) {
    query.value = s
    }
    val isOpen = MutableStateFlow(false)
    val currentWallpaperCount = MutableLiveData(0)
    val currentPosition = MutableLiveData(1)
    var currentWallPaper: Bitmap? = null
    private fun getWallpapers(query: String): Flow<Resource<WallpaperResponse>> = flow {
    currentPosition.value = 1
    emit(Resource.Loading<WallpaperResponse>())
    try {
    val wallpaperResponse = pixaBayAPI.getWallpapers(query = query)
    emit(Resource.Success(wallpaperResponse))
    } catch (exception: Exception) {
    emit(Resource.Error<WallpaperResponse>(exception))
    }
    }
    }  8) Creating the layouts – Now we will begin creating the layouts. Go to res -> layout. Right Click and make a new Layout resource file and name it “wallpaper_display_layout” Put the following code in the file – <?xml version=”1.0″ encoding=”utf-8″?>
    <RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”>

    <ImageView
    android:id=”@+id/image_view”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    android:scaleType=”fitXY” />
    </RelativeLayout>   This will be the layout for displaying the wallpapers in the image slider. We have a single imageview in the relative layout which takes up full width and height of the screen.  Next, we create the layout for the Wallpapers fragment. Create a new layout resource file like we did earlier and name it “fragment_wallpapers”  Create a new package in the root directory and name it ui. Now in the ui package, create a new class and name it “WallpapersFragment”    Make the class inherit from the Fragment class (use androidx.fragment package import) and pass the layout into the constructor – R.layout.fragment_wallpapers. Also, annotate the class with “@AndroidEntryPoint”. This tells dagger – hilt that dependencies can be injected into this Fragment.     Now, lets come back to the fragment_wallpapers file. Add the following xml code –  <?xml version=”1.0″ encoding=”utf-8″?>
    <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=”http://schemas.android.com/apk/res/android”
    xmlns:app=”http://schemas.android.com/apk/res-auto”
    xmlns:tools=”http://schemas.android.com/tools”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    tools:context=”.ui.WallpapersFragment”>

    <ProgressBar
    android:id=”@+id/progress_bar”
    android:layout_width=”wrap_content”
    android:layout_gravity=”center”
    android:layout_height=”wrap_content”
    android:layout_centerInParent=”true” />

    <TextView
    android:id=”@+id/error_text_view”
    android:layout_width=”match_parent”
    android:layout_height=”wrap_content”
    android:layout_centerInParent=”true”
    android:textAlignment=”center”
    android:layout_gravity=”center”
    android:textColor=”@color/black” />

    <androidx.cardview.widget.CardView
    android:layout_width=”wrap_content”
    android:layout_gravity=”top|end”
    android:layout_height=”wrap_content”
    android:padding=”10dp”
    android:layout_marginTop=”20dp”
    android:layout_marginEnd=”30dp”
    app:cardCornerRadius=”10dp”
    android:background=”@color/white”>
    <TextView
    android:id=”@+id/count_text_view”
    android:layout_marginTop=”5dp”
    android:layout_marginBottom=”5dp”
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_marginStart=”10dp”
    android:layout_marginEnd=”10dp”
    android:textColor=”@color/black”
    tools:text=”12/30″
    android:textSize=”20sp” />
    </androidx.cardview.widget.CardView>

    <com.smarteist.autoimageslider.SliderView
    android:id=”@+id/imageSlider”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    app:sliderAnimationDuration=”600″
    app:sliderAutoCycleDirection=”back_and_forth”
    app:sliderAutoCycleEnabled=”true”
    app:sliderIndicatorAnimationDuration=”600″
    app:sliderIndicatorGravity=”center_horizontal|bottom”
    app:sliderIndicatorMargin=”15dp”
    app:sliderIndicatorOrientation=”horizontal”
    app:sliderIndicatorPadding=”3dp”
    app:sliderIndicatorRadius=”2dp”
    app:sliderIndicatorSelectedColor=”#0000ffff”
    app:sliderIndicatorUnselectedColor=”#0000ffff” />
    <View
    android:id=”@+id/fabBGLayout”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    android:background=”@color/colorTransBg”
    android:visibility=”gone” />

    <LinearLayout
    android:id=”@+id/fabLayout1″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_gravity=”bottom|end”
    android:layout_marginEnd=”@dimen/standard_23″
    android:layout_marginBottom=”@dimen/standard_23″
    android:clipToPadding=”false”
    android:gravity=”center_vertical”
    android:padding=”@dimen/standard_12″
    android:visibility=”gone”>

    <TextView
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:text=”Set as home wallpaper”
    android:textColor=”@color/white” />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id=”@+id/fab1″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_marginStart=”10dp”
    app:fabSize=”mini”
    app:srcCompat=”@drawable/home_icon” />
    </LinearLayout>

    <LinearLayout
    android:id=”@+id/fabLayout2″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_gravity=”bottom|end”
    android:layout_marginEnd=”@dimen/standard_23″
    android:layout_marginBottom=”@dimen/standard_23″
    android:clipToPadding=”false”
    android:gravity=”center_vertical”
    android:padding=”@dimen/standard_12″
    android:visibility=”gone”>

    <TextView
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:text=”Set as Lock Screen”
    android:textColor=”@color/white” />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id=”@+id/fab2″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_marginStart=”10dp”
    app:fabSize=”mini”
    app:srcCompat=”@drawable/lock_icon” />
    </LinearLayout>

    <LinearLayout
    android:id=”@+id/fabLayout3″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_gravity=”bottom|end”
    android:layout_marginEnd=”@dimen/standard_23″
    android:layout_marginBottom=”@dimen/standard_23″
    android:clipToPadding=”false”
    android:gravity=”center_vertical”
    android:padding=”@dimen/standard_12″
    android:visibility=”gone”>

    <TextView
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:text=”Set as Home and Lock”
    android:textColor=”@color/white” />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id=”@+id/fab3″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_marginStart=”10dp”
    android:tint=”@android:color/white”
    app:fabSize=”mini”
    app:srcCompat=”@drawable/phone_icon” />
    </LinearLayout>

    <LinearLayout
    android:id=”@+id/fabLayout4″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_gravity=”bottom|end”
    android:layout_marginEnd=”@dimen/standard_23″
    android:layout_marginBottom=”@dimen/standard_23″
    android:clipToPadding=”false”
    android:gravity=”center_vertical”
    android:padding=”@dimen/standard_12″
    android:visibility=”gone”>

    <TextView
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:text=”Download”
    android:textColor=”@color/white” />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id=”@+id/fab4″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_marginStart=”10dp”
    app:fabSize=”mini”
    app:srcCompat=”@drawable/download_icon” />
    </LinearLayout>

    <LinearLayout
    android:id=”@+id/fabLayout5″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_gravity=”bottom|end”
    android:layout_marginEnd=”@dimen/standard_23″
    android:layout_marginBottom=”@dimen/standard_23″
    android:clipToPadding=”false”
    android:gravity=”center_vertical”
    android:padding=”@dimen/standard_12″
    android:visibility=”gone”>

    <TextView
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:text=”Share”
    android:textColor=”@color/white” />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id=”@+id/fab5″
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_marginStart=”10dp”
    app:fabSize=”mini”
    app:srcCompat=”@drawable/share_icon” />
    </LinearLayout>

    <LinearLayout
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_gravity=”bottom|end”
    android:layout_marginEnd=”@dimen/fab_margin”
    android:layout_marginBottom=”@dimen/fab_margin”
    android:clipToPadding=”false”
    android:gravity=”center_vertical”
    android:padding=”@dimen/standard_12″>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id=”@+id/fab”
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
    android:layout_gravity=”bottom|end”
    android:gravity=”center_vertical”
    app:fabSize=”normal”
    app:srcCompat=”@drawable/toys_icon” />
    </LinearLayout>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>    9)Image Slider Adapter – We will now create an image slider which will take the Wallpapers and show it in a image slider.  Create a new package in the root directory and name it adapters. Create a new class named SliderAdapter. The Slider Adapter will inherit from the SliderViewAdapter class. The code is quite similar to the standard recycleviewThe “list” variable stores the wallpapers.     We create the onCreateViewHolder method which returns the SliderViewHolder object. We pass an instance of the WallpaperDisplayLayoutBinding to the constructor.   Next we create the onBindViewHolder. It takes a wallpaper and calls the “bind” function to attach the wallpaper to the UI.   Next , we create the submitList function, which updates the wallpapers list –   Now, create the getCount function which returns the list size.      In the viewHolder, we only have one function “bind” which displays the wallpaper in the imageview. We use Glide image processing library for displaying images.   10) Working on the fragment –  First we get our Wallpaper viewModel by using kotlin viewModel property delegate syntax. The viewModel gets inject automatically into our fragment.  Also, create the binding variable which will be an instance of the FragmentWallpapersBinding.    Override the onViewCreated method and add the following code –  @RequiresApi(Build.VERSION_CODES.Q)
    @SuppressLint(“SetTextI18n”)
    @ExperimentalCoroutinesApi
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    _binding = FragmentWallpapersBinding.bind(view)
    val sliderAdapter = SliderAdapter()
    binding.apply {
    imageSlider.setSliderAdapter(sliderAdapter)
    viewLifecycleOwner.lifecycleScope.launchWhenStarted {
    wallpaperViewModel.wallpapers.collectLatest { result ->
    if (result == null) return@collectLatest
    result.data?.wallpapers?.let {
    sliderAdapter.submitList(it)
    wallpaperViewModel.currentWallpaperCount.value = result.data.total
    }
    progressBar.isVisible =
    result is Resource.Loading && result.data == null
    errorTextView.isVisible =
    result is Resource.Error && result.data == null
    errorTextView.text = result.error?.localizedMessage
    }
    }
    viewLifecycleOwner.lifecycleScope.launchWhenStarted {
    fab.setOnClickListener {
    if (!wallpaperViewModel.isOpen.value) {
    openFab()
    } else {
    closeFab()
    }
    }
    fabBGLayout.setOnClickListener {
    closeFab()
    }
    }
    wallpaperViewModel.currentPosition.observe(viewLifecycleOwner) {
    countTextView.text =
    it.toString() + “/” + wallpaperViewModel.currentWallpaperCount.value.toString()
    }
    wallpaperViewModel.currentWallpaperCount.observe(viewLifecycleOwner) {
    countTextView.text =
    wallpaperViewModel.currentPosition.value.toString() + “/” + it
    }
    imageSlider.setCurrentPageListener {
    wallpaperViewModel.currentPosition.value = it + 1
    }
    fab1.setOnClickListener {
    val wallPaperManager = WallpaperManager.getInstance(requireContext())
    wallpaperViewModel.currentWallPaper = getBitmapFromView(imageSlider)
    if (wallpaperViewModel.currentWallPaper == null) {
    Toast.makeText(
    requireContext(),
    “Some error occured, please try again later”,
    Toast.LENGTH_SHORT
    ).show()
    } else {
    wallPaperManager.setBitmap(
    wallpaperViewModel.currentWallPaper, null, false,
    WallpaperManager.FLAG_SYSTEM
    )
    Toast.makeText(requireContext(), “WallPaper set”, Toast.LENGTH_SHORT).show()
    }
    }
    fab2.setOnClickListener {
    val wallPaperManager = WallpaperManager.getInstance(requireContext())
    wallpaperViewModel.currentWallPaper = getBitmapFromView(imageSlider)
    if (wallpaperViewModel.currentWallPaper == null) {
    Toast.makeText(
    requireContext(),
    “Some error occured, please try again later”,
    Toast.LENGTH_SHORT
    ).show()
    } else {
    wallPaperManager.setBitmap(
    wallpaperViewModel.currentWallPaper, null, false,
    WallpaperManager.FLAG_LOCK
    )
    Toast.makeText(requireContext(), “WallPaper set”, Toast.LENGTH_SHORT).show()
    }
    }
    fab3.setOnClickListener {
    val wallPaperManager = WallpaperManager.getInstance(requireContext())
    wallpaperViewModel.currentWallPaper = getBitmapFromView(imageSlider)
    if (wallpaperViewModel.currentWallPaper == null) {
    Toast.makeText(
    requireContext(),
    “Some error occured, please try again later”,
    Toast.LENGTH_SHORT
    ).show()
    } else {
    wallPaperManager.setBitmap(
    wallpaperViewModel.currentWallPaper, null, false,
    WallpaperManager.FLAG_LOCK or WallpaperManager.FLAG_SYSTEM
    )
    Toast.makeText(requireContext(), “WallPaper set”, Toast.LENGTH_SHORT).show()
    }
    }
    fab4.setOnClickListener {
    wallpaperViewModel.currentWallPaper = getBitmapFromView(imageSlider)
    val fos: OutputStream?
    try {
    val contentResolver: ContentResolver = requireActivity().contentResolver
    val contentValues = ContentValues()
    contentValues.put(
    MediaStore.MediaColumns.DISPLAY_NAME,
    System.currentTimeMillis().toString() + “.jpg”
    )
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, “image/jpeg”)
    contentValues.put(
    MediaStore.MediaColumns.RELATIVE_PATH,
    Environment.DIRECTORY_PICTURES + File.separator + “Wallpaper App Clone”
    )
    val imageUri = contentResolver.insert(
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    contentValues
    )
    fos = contentResolver.openOutputStream(imageUri!!) as FileOutputStream?
    wallpaperViewModel.currentWallPaper!!.compress(
    Bitmap.CompressFormat.JPEG,
    100,
    fos
    )
    Objects.requireNonNull<OutputStream?>(fos)
    Toast.makeText(requireContext(), “WallPaper downloaded”, Toast.LENGTH_SHORT)
    .show()
    } catch (e: Exception) {
    Toast.makeText(requireContext(), e.localizedMessage, Toast.LENGTH_SHORT).show()
    }
    }
    fab5.setOnClickListener {
    wallpaperViewModel.currentWallPaper = getBitmapFromView(imageSlider)
    try {
    val bytes = ByteArrayOutputStream()
    wallpaperViewModel.currentWallPaper!!.compress(
    Bitmap.CompressFormat.JPEG,
    100,
    bytes
    )
    val path: String = MediaStore.Images.Media.insertImage(
    requireActivity().contentResolver,
    wallpaperViewModel.currentWallPaper,
    “Wallpaper_” + System.currentTimeMillis(),
    “Wallpaper new”
    )
    val imageUri = Uri.parse(path)

    val waIntent = Intent(Intent.ACTION_SEND)
    waIntent.type = “image/*”
    waIntent.putExtra(Intent.EXTRA_STREAM, imageUri)
    startActivity(Intent.createChooser(waIntent, “Share with”))
    } catch (e: Exception) {
    Toast.makeText(
    requireContext(),
    e.localizedMessage ?: “An error occurred”,
    Toast.LENGTH_SHORT
    ).show()
    }
    }
    }
    setHasOptionsMenu(true)
    }  The following code snippet sets the wallpaper as home screen –  When the user clicks on the fab1 (floating action button) , we download the currentWallpaper as a bitmap and then use the wallpaper manager class from to set it as home screen. Similarly, we on clicking fab2,  we execute the following code –   This time, we pass “FLAG_LOCK” as an argument to the wallpaper manager.  And when fab3 is clicked, we pass WallpaperManager.FLAG_LOCK or WallpaperManager.FLAG_SYSTEM which sets the wallpaper to the lock and home screen. This is how the floating action menu will look –   Now we create our searchbar –   override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    super.onCreateOptionsMenu(menu, inflater)
    inflater.inflate(R.menu.search_menu, menu)
    val searchItem = menu.findItem(R.id.action_search)
    val searchView = searchItem.actionView as SearchView

    searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
    override fun onQueryTextSubmit(query: String?): Boolean {

    if (query != null) {
    wallpaperViewModel.searchWallPapers(query)
    searchView.clearFocus()
    }
    return true
    }

    override fun onQueryTextChange(newText: String?): Boolean {
    return true
    }
    })
    } Everytime the query is submitted, the searchWallpapers function is called in the viewModel.   Now we create the functions for opening and closing the floating action menu –  private fun openFab() {
    binding.apply {
    wallpaperViewModel.isOpen.value = true
    fabLayout1.isVisible = true
    fabLayout2.isVisible = true
    fabLayout3.isVisible = true
    fabLayout4.isVisible = true
    fabLayout5.isVisible = true
    fab.animate().rotationBy(180F)
    fabLayout1.animate().translationY(-resources.getDimension(R.dimen.standard_55))
    fabLayout2.animate().translationY(-resources.getDimension(R.dimen.standard_100))
    fabLayout3.animate().translationY(-resources.getDimension(R.dimen.standard_145))
    fabLayout4.animate().translationY(-resources.getDimension(R.dimen.standard_190))
    fabLayout5.animate().translationY(-resources.getDimension(R.dimen.standard_235))
    }
    }

    private fun closeFab() {
    binding.apply {
    wallpaperViewModel.isOpen.value = false
    fabBGLayout.isVisible = false
    fab.animate().rotation(0F)
    fabLayout1.animate().translationY(0F)
    fabLayout2.animate().translationY(0F)
    fabLayout3.animate().translationY(0F)
    fabLayout4.animate().translationY(0F)
    fabLayout5.animate().translationY(0F)
    fabLayout5.animate().translationY(0F).setListener(object : Animator.AnimatorListener {
    override fun onAnimationStart(p0: Animator?) {

    }

    override fun onAnimationEnd(p0: Animator?) {
    if (!wallpaperViewModel.isOpen.value) {
    fabLayout1.isVisible = false
    fabLayout2.isVisible = false
    fabLayout3.isVisible = false
    fabLayout4.isVisible = false
    fabLayout5.isVisible = false
    }
    }

    override fun onAnimationCancel(p0: Animator?) {

    }

    override fun onAnimationRepeat(p0: Animator?) {

    }
    })
    }
    }  In the end, we create the function for getting the wallpaper as a bitmap.  private fun getBitmapFromView(view: View): Bitmap? {
    val bitmap =
    Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    view.draw(canvas)
    return bitmap
    } And finally, we override the onDestroyView Method – override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
    } 11) Final touches – Add the following code to the activity_main.xml – <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=”http://schemas.android.com/apk/res/android”
    xmlns:app=”http://schemas.android.com/apk/res-auto”
    xmlns:tools=”http://schemas.android.com/tools”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    tools:context=”.MainActivity”>

    <androidx.fragment.app.FragmentContainerView
    android:id=”@+id/nav_host_fragment_main”
    android:name=”androidx.navigation.fragment.NavHostFragment”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    app:defaultNavHost=”true”
    app:navGraph=”@navigation/nav_graph” />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>  And add the following code to the MainAcitivty.kt file –  @AndroidEntryPoint
    class MainActivity : AppCompatActivity() {
    private lateinit var navController: NavController
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
    val navHostFragment =
    supportFragmentManager.findFragmentById(R.id.nav_host_fragment_main) as NavHostFragment
    navController = navHostFragment.findNavController()

    val appBarConfiguration = AppBarConfiguration(navController.graph)
    setupActionBarWithNavController(navController, appBarConfiguration)
    }

    override fun onSupportNavigateUp(): Boolean {
    return navController.navigateUp() || super.onSupportNavigateUp()
    }
    } Now in the root package, create a new class called “HomeApplication” and put this code into it – @HiltAndroidApp
    class HomeApplication: Application() {
    } This class tells dagger-hilt to start generating the code for dependency injection at runtime. We also need to register this class in the Android Manifest file  by adding this line –   Also, add the following permissions in the manifest –  <uses-permission android:name=”android.permission.SET_WALLPAPER” />
    <uses-permission android:name=”android.permission.INTERNET” />
    <uses-permission
    android:name=”android.permission.WRITE_EXTERNAL_STORAGE”
    android:maxSdkVersion=”30″
    tools:ignore=”ScopedStorage” />
    <uses-permission android:name=”android.permission.READ_EXTERNAL_STORAGE” />

 

January 14, 2022

0 responses on "How to Build Your Own Wallpaper Setting App"

Leave a Message