How to Build a Sticky Notes Application in Android Studio?
We as humans tend to forget some small but important things, and to resolve this we try to write those things up and paste them somewhere, we often have eyes on. And in this digital era, the things we are most likely to see are laptop, computer, or mobile phone screens. For this, we all have once used the Sticky Notes on Windows or Stickies on Mac, today in this article we would be discussing how we can build such an application for Android.
Approach :
Now, if we think of something that is sticky i.e. which sticks to the screen, in Android, the component which comes to our mind is the Home screen Widgets. Home Screen Widgets are the best interactive components that stick to the screen and can be resized in any manner. We would be creating an application that also has its own widget. We will first write some text in the main application and save that into a file in memory. At the very moment, we will update the widget with text that the user just saved. And Hurray! Our Sticky note is ready.
What is a Home Screen Widget?
Here one thing to note is that in Android views are also called Widgets, so to avoid confusion we use the term Home Screen Widgets. These are the broadcast receivers that provide interactive components. They usually show some sort of information and encourage the user to interact with it.
For example, it may display time, weather, or emails and once you click on them the main application hosting them would start. Widgets use RemoteViews to build its user interface. With the same permissions as the original application, a RemoteView may be run by another method. As a consequence, the widget runs with the permissions of the application that created it. A Widget's user interface is determined by a broadcast receiver. This receiver inflates its layout into a RemoteView object.
Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio.
Note that select Java/Kotlin as the programming language.
Step 2: Working with the AndroidManifest.xml file
Navigate to app > manifests > AndroidManifest.xml and add the user permissions for reading and write the file from storage.
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
Step 3: Creating a Widget
Follow the steps mentioned in this article Create a Basic Widget of an Android App to add the Widget to your project. Name the file as AppWidget and leave the other settings as it is. The file app_widget_info.xml contains code that determines the look of our widget as it would appear in the Widgets list.
Step 4: Working with the AppWidget Class
This class will be formed in the previous step. Go to the AppWidget.java/.kt file and refer to the following code. Below is the code for the AppWidget file. Comments are added inside the code to understand the code in more detail.
AppWidget File:
package org.geeksforgeeks.demo;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
// Implementation of App Widget functionality
public class AppWidget extends AppWidgetProvider {
// Create an Intent to launch activity
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// Perform this loop procedure for each App Widget that belongs to this provider
for (int appWidgetId : appWidgetIds) {
// Create an Intent to launch MainActivity
Intent launchActivity = new Intent(context, MainActivity.class);
launchActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Attaching a Pending Intent to trigger it when application is not alive
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
0,
launchActivity,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget);
remoteViews.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
}
package org.geeksforgeeks.demo
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
// Implementation of App Widget functionality
class AppWidget : AppWidgetProvider() {
// Create an Intent to launch activity
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
// Perform this loop procedure for each App Widget
// that belongs to this provider
for (appWidgetId in appWidgetIds) {
// Create an Intent to launch MainActivity
val launchActivity = Intent(
context,
MainActivity::class.java
).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
// Attaching a Pending Intent
// to trigger it when
// application is not alive
val pendingIntent = PendingIntent.getActivity(context, 0, launchActivity,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
// Get the layout for the App Widget and attach
// an on-click listener to the button
val remoteViews = RemoteViews(context.packageName, R.layout.app_widget)
remoteViews.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent)
// Tell the AppWidgetManager to perform an
// update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
}
}
}
Step 5: Creating the StickyNote Class
This is a helper class that provides the functionality of saving, updating, and retrieving the contents to and from the file. Go to the StickyNote file and refer to the following code. Below is the code for the StickyNote file. Comments are added inside the code to understand the code in more detail.
StickyNote File:
package org.geeksforgeeks.demo;
import android.content.Context;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import static android.content.Context.MODE_PRIVATE;
class StickyNote {
// this function will return
// the content from the text
// file(if any)
String getStick(Context context) {
// get the file from path
File fileEvents = new File(context.getFilesDir().getPath() + "/gfg.txt");
// create a StringBuilder to store the text from file
StringBuilder text = new StringBuilder();
try {
// use the BufferedReader
// to Read the file
// efficiently
BufferedReader br = new BufferedReader(new FileReader(fileEvents));
String line;
// read a single line at a time
// append newline character after each line
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
// close the BufferedReader
br.close();
} catch (IOException e) {
e.printStackTrace();
}
// finally return the
// string i.e. the retrieved data
// from file
return text.toString();
}
// this function saves the new
// content in the file if it
// exists or will create a new one
void setStick(String textToBeSaved, Context context) {
// create the FileOutputStream
// to efficiently write the file
FileOutputStream fos = null;
try {
// get the file from storage
fos = context.getApplicationContext().openFileOutput("gfg.txt", MODE_PRIVATE);
// write to the file at once
fos.write(textToBeSaved.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package org.geeksforgeeks.demo
import android.content.Context
import java.io.BufferedReader
import java.io.File
import java.io.FileOutputStream
import java.io.FileReader
import java.io.IOException
class StickyNote {
// this function will return
// the content from the text
// file(if any)
fun getStick(context: Context): String {
// get the file from path
val fileEvents = File(context.filesDir.path + "/gfg.txt")
// create a StringBuilder to store the text from file
val text = StringBuilder()
try {
// use the BufferedReader
// to Read the file
// efficiently
val br = BufferedReader(FileReader(fileEvents))
var line: String?
// read a single line at a time
// append newline character after each line
while ((br.readLine().also { line = it }) != null) {
text.append(line)
text.append('\n')
}
// close the BufferedReader
br.close()
} catch (e: IOException) {
e.printStackTrace()
}
// finally return the
// string i.e. the retrieved data
// from file
return text.toString()
}
// this function saves the new
// content in the file if it
// exists or will create a new one
fun setStick(textToBeSaved: String, context: Context) {
// create the FileOutputStream
// to efficiently write the file
var fos: FileOutputStream? = null
try {
// get the file from storage
fos = context.applicationContext.openFileOutput("gfg.txt", Context.MODE_PRIVATE)
// write to the file at once
fos.write(textToBeSaved.toByteArray())
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (fos != null) {
try {
fos.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
}
Step 6: Working with activity_main.xml
Navigate to app > res > layout > activity_main.xml and add the following code. Also remember to create a new drawable file for the save button and copy the code from below for ic_save.xml.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
android:background="@color/white"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:gravity="start|top"
android:padding="32dp"
android:hint="Enter your text here"
android:inputType="textMultiLine"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/white"
android:textColorHint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floating_save_button"
style="@style/Widget.Design.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:backgroundTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_save" />
</androidx.constraintlayout.widget.ConstraintLayout>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M840,280v480q0,33 -23.5,56.5T760,840L200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h480l160,160ZM760,314L646,200L200,200v560h560v-446ZM480,720q50,0 85,-35t35,-85q0,-50 -35,-85t-85,-35q-50,0 -85,35t-35,85q0,50 35,85t85,35ZM240,400h360v-160L240,240v160ZM200,314v446,-560 114Z"
android:fillColor="#e8eaed"/>
</vector>
Design UI:

Step 7: Working with the MainActivity file
Go to the MainActivity file and refer to the following code. Below is the code for the MainActivity file. Comments are added inside the code to understand the code in more detail.
MainActivity File:
package org.geeksforgeeks.demo;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.RemoteViews;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
public class MainActivity extends AppCompatActivity {
private EditText mEditText;
// Creating a new note
private final StickyNote note = new StickyNote();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Getting the reference of the EditText
mEditText = findViewById(R.id.editText);
FloatingActionButton save = findViewById(R.id.floating_save_button);
// Retrieve the text from the saved file in memory (if any) and set it to the EditText
mEditText.setText(note.getStick(this));
save.setOnClickListener(view -> saveButton());
}
// Function to update the Widget (Sticky Note) every time the user saves the note
private void updateWidget() {
// The AppWidgetManager helps us manage all the widgets from this app
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
// The RemoteViews class allows us to inflate a layout resource file hierarchy
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.app_widget);
// By using ComponentName class, we can get a specific application component
ComponentName thisWidget = new ComponentName(this, AppWidget.class);
// Update the text of the TextView of the widget
remoteViews.setTextViewText(R.id.appwidget_text, mEditText.getText().toString());
// Finally, use the AppWidgetManager instance to update all the widgets
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
finish();
}
// This function saves the current status of the EditText
private void saveButton() {
// Update the content of the file stored in memory
note.setStick(mEditText.getText().toString(), this);
updateWidget();
Toast.makeText(this, "Updated Successfully!!", Toast.LENGTH_SHORT).show();
}
}
package org.geeksforgeeks.demo
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.os.Bundle
import android.widget.EditText
import android.widget.RemoteViews
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton
class MainActivity : AppCompatActivity() {
private lateinit var mEditText: EditText
private lateinit var save: FloatingActionButton
// creating a new note
private var note: StickyNote = StickyNote()
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
super.onCreate(savedInstanceState)
// getting the reference of the EditText
mEditText = findViewById(R.id.editText)
save = findViewById(R.id.floating_save_button)
// retrieve the text from the saved file in
// memory(if any) and set it to the edittext
mEditText.setText(note.getStick(this))
save.setOnClickListener {
saveButton()
}
}
// function to update the
// Widget(Sticky Note) every time
// user saves the note
private fun updateWidget() {
// the AppWidgetManager helps
// us to manage all the
// widgets from this app
val appWidgetManager = AppWidgetManager.getInstance(this)
// the RemoteViews class allows us to inflate a
// layout resource file hierarchy and provides some
// basic operations for modifying the content of the
// inflated hierarchy
val remoteViews = RemoteViews(this.packageName, R.layout.app_widget)
// by using ComponentName class we can get specific
// application component and to identify the
// component we pass the package name and the class
// name inside of that package
val thisWidget = ComponentName(this, AppWidget::class.java)
// update the text of the textview of the widget
remoteViews.setTextViewText(R.id.appwidget_text, mEditText.text.toString())
// finally us the AppWidgetManager instance to
// update all the widgets
appWidgetManager.updateAppWidget(thisWidget, remoteViews)
finish()
}
// this function saves
// the current status
// of the EditText
private fun saveButton() {
// update the content of file stored in the memory
note.setStick(mEditText.text.toString(), this)
updateWidget()
Toast.makeText(this, "Updated Successfully!!", Toast.LENGTH_SHORT).show()
}
}
Step 8: Working with Widget Layout file
Navigate to app > res > layout > app_widget.xml. Since we only want our widget to hold the text, we just add the TextView in the layout resource file which would be updated from time to time.
app_widget.xml:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.Demo.AppWidget.Container"
android:id="@+id/widgetLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FDD835"
android:orientation="horizontal"
android:padding="8dp"
android:theme="@style/Theme.Demo.AppWidgetContainer">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/appwidget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
android:textColor="@color/white"
android:textSize="20sp"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp" />
</LinearLayout>
</FrameLayout>
Design UI:

Refer to the github repo to get the entire code: Sticky_Notes_Android_Application
Output:
Future Scope
- You can add functionality to change the appearance of the Widget, like the text style, background color, transparency, width, etc.
- You can make some UI improvements.
- You can create multiple Sticky Notes, by saving the data into the database and fetching the same.
- Also, you can try adding some different views to the Widget.