summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/AndroidManifest.xml4
-rw-r--r--app/src/main/java/app/olauncher/MainActivity.kt26
-rw-r--r--app/src/main/java/app/olauncher/MainViewModel.kt58
-rw-r--r--app/src/main/java/app/olauncher/data/AppModel.kt10
-rw-r--r--app/src/main/java/app/olauncher/helper/Utils.kt65
-rw-r--r--app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt105
-rw-r--r--app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt46
-rw-r--r--app/src/main/res/layout/adapter_private_space_header.xml21
-rw-r--r--app/src/main/res/values/strings.xml1
9 files changed, 305 insertions, 31 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b81f8a8..91aa7af 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
- tools:ignore="QueryAllPackagesPermission" />
+ tools:ignore="PackageVisibilityPolicy,QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<!--Used only for downloading wallpapers-->
<uses-permission android:name="android.permission.INTERNET" />
@@ -15,6 +15,8 @@
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
+ <!--Used for Private Space support on Android 15+-->
+ <uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
<application
android:allowBackup="true"
diff --git a/app/src/main/java/app/olauncher/MainActivity.kt b/app/src/main/java/app/olauncher/MainActivity.kt
index 4f2b0f9..1f081b2 100644
--- a/app/src/main/java/app/olauncher/MainActivity.kt
+++ b/app/src/main/java/app/olauncher/MainActivity.kt
@@ -2,8 +2,10 @@ package app.olauncher
import android.annotation.SuppressLint
import android.app.Activity
+import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Build
@@ -51,6 +53,7 @@ class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private var timerJob: Job? = null
private var isResumed = false
+ private var profileReceiver: BroadcastReceiver? = null
// override fun onBackPressed() {
// if (navController.currentDestination?.id != R.id.mainFragment)
@@ -105,6 +108,19 @@ class MainActivity : AppCompatActivity() {
setupOrientation()
window.addFlags(FLAG_LAYOUT_NO_LIMITS)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ profileReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ viewModel.getPrivateSpaceAppList()
+ }
+ }
+ val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
+ }
+ registerReceiver(profileReceiver, filter)
+ }
}
override fun onStart() {
@@ -346,6 +362,16 @@ class MainActivity : AppCompatActivity() {
}
}
+ override fun onDestroy() {
+ profileReceiver?.let {
+ try {
+ unregisterReceiver(it)
+ } catch (_: Exception) {
+ }
+ }
+ super.onDestroy()
+ }
+
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
diff --git a/app/src/main/java/app/olauncher/MainViewModel.kt b/app/src/main/java/app/olauncher/MainViewModel.kt
index 518cbc8..e513f7b 100644
--- a/app/src/main/java/app/olauncher/MainViewModel.kt
+++ b/app/src/main/java/app/olauncher/MainViewModel.kt
@@ -3,8 +3,11 @@ package app.olauncher
import android.app.Application
import android.content.ComponentName
import android.content.Context
+import android.content.Intent
import android.content.pm.LauncherApps
+import android.os.Build
import android.os.UserHandle
+import android.os.UserManager
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
@@ -21,9 +24,12 @@ import app.olauncher.helper.SingleLiveEvent
import app.olauncher.helper.WallpaperWorker
import app.olauncher.helper.formattedTimeSpent
import app.olauncher.helper.getAppsList
+import app.olauncher.helper.getPrivateSpaceApps
+import app.olauncher.helper.getPrivateSpaceUserHandle
import app.olauncher.helper.hasBeenMinutes
import app.olauncher.helper.isOlauncherDefault
import app.olauncher.helper.isPackageInstalled
+import app.olauncher.helper.isPrivateSpaceLocked
import app.olauncher.helper.showToast
import app.olauncher.helper.usageStats.EventLogWrapper
import kotlinx.coroutines.launch
@@ -46,18 +52,25 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val homeAppAlignment = MutableLiveData<Int>()
val screenTimeValue = MutableLiveData<String>()
+ val privateSpaceApps = MutableLiveData<List<AppModel>?>()
+ val privateSpaceLocked = MutableLiveData<Boolean>()
+ val privateSpaceAvailable = MutableLiveData<Boolean>()
+
val showDialog = SingleLiveEvent<String>()
val checkForMessages = SingleLiveEvent<Unit?>()
val resetLauncherLiveData = SingleLiveEvent<Unit?>()
val showRecentApps = SingleLiveEvent<Unit?>()
fun selectedApp(appModel: AppModel, flag: Int) {
+ if (appModel is AppModel.PrivateSpaceHeader) return
when (flag) {
Constants.FLAG_LAUNCH_APP -> {
when (appModel) {
is AppModel.PinnedShortcut -> launchShortcut(appModel)
is AppModel.App ->
launchApp(appModel.appPackage, appModel.activityClassName, appModel.user)
+
+ else -> {}
}
}
@@ -97,6 +110,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
private fun saveHomeApp(appModel: AppModel, position: Int) {
when (appModel) {
+ is AppModel.PrivateSpaceHeader -> return
is AppModel.App -> {
when (position) {
1 -> {
@@ -254,6 +268,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
private fun saveSwipeApp(appModel: AppModel, isLeft: Boolean) {
when (appModel) {
+ is AppModel.PrivateSpaceHeader -> return
is AppModel.App -> {
if (isLeft) {
prefs.appNameSwipeLeft = appModel.appLabel
@@ -372,6 +387,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val apps = getAppsList(appContext, prefs, includeRegularApps = true, includeHiddenApps)
appList.value = apps
}
+ getPrivateSpaceAppList()
}
fun getHiddenApps() {
@@ -439,6 +455,48 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
prefs.screenTimeLastUpdated = endTime
}
+ fun getPrivateSpaceAppList() {
+ viewModelScope.launch {
+ val handle = getPrivateSpaceUserHandle(appContext)
+ privateSpaceAvailable.value = handle != null
+ if (handle != null) {
+ privateSpaceLocked.value = isPrivateSpaceLocked(appContext, handle)
+ privateSpaceApps.value = getPrivateSpaceApps(appContext, prefs)
+ } else {
+ privateSpaceLocked.value = true
+ privateSpaceApps.value = emptyList()
+ }
+ }
+ }
+
+ fun openPrivateSpaceSettings() {
+ try {
+ val intent = Intent("android.settings.PRIVATE_SPACE_SETTINGS")
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ appContext.startActivity(intent)
+ } catch (_: Exception) {
+ try {
+ val intent = Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ appContext.startActivity(intent)
+ } catch (_: Exception) {
+ appContext.showToast(appContext.getString(R.string.unable_to_open_app))
+ }
+ }
+ }
+
+ fun togglePrivateSpaceLock() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) return
+ val handle = getPrivateSpaceUserHandle(appContext) ?: return
+ try {
+ val userManager = appContext.getSystemService(Context.USER_SERVICE) as UserManager
+ val currentlyLocked = userManager.isQuietModeEnabled(handle)
+ userManager.requestQuietModeEnabled(!currentlyLocked, handle)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
fun setDefaultClockApp() {
viewModelScope.launch {
try {
diff --git a/app/src/main/java/app/olauncher/data/AppModel.kt b/app/src/main/java/app/olauncher/data/AppModel.kt
index 71397a9..6df3083 100644
--- a/app/src/main/java/app/olauncher/data/AppModel.kt
+++ b/app/src/main/java/app/olauncher/data/AppModel.kt
@@ -28,6 +28,16 @@ sealed class AppModel : Comparable<AppModel> {
override val user: UserHandle,
) : AppModel()
+ data class PrivateSpaceHeader(
+ val isLocked: Boolean = true,
+ override val user: UserHandle = android.os.Process.myUserHandle(),
+ ) : AppModel() {
+ override val appLabel: String = ""
+ override val key: CollationKey? = null
+ override val appPackage: String = ""
+ override val isNew: Boolean = false
+ }
+
override fun compareTo(other: AppModel): Int = when {
key != null && other.key != null -> key!!.compareTo(other.key)
else -> appLabel.compareTo(other.appLabel, true)
diff --git a/app/src/main/java/app/olauncher/helper/Utils.kt b/app/src/main/java/app/olauncher/helper/Utils.kt
index 372effe..d2f45cb 100644
--- a/app/src/main/java/app/olauncher/helper/Utils.kt
+++ b/app/src/main/java/app/olauncher/helper/Utils.kt
@@ -85,6 +85,7 @@ suspend fun getAppsList(
val collator = Collator.getInstance()
for (profile in userManager.userProfiles) {
+ if (isPrivateSpaceProfile(context, profile)) continue
for (app in launcherApps.getActivityList(null, profile)) {
val appLabelShown = prefs.getAppRenameLabel(app.applicationInfo.packageName)
.ifBlank { app.label.toString() }
@@ -189,10 +190,72 @@ private fun upgradeHiddenApps(prefs: Prefs) {
fun isPackageInstalled(context: Context, packageName: String, userString: String): Boolean {
val launcher = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val activityInfo = launcher.getActivityList(packageName, getUserHandleFromString(context, userString))
- if (activityInfo.size > 0) return true
+ if (activityInfo.isNotEmpty()) return true
return false
}
+fun isPrivateSpaceProfile(context: Context, userHandle: UserHandle): Boolean {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) return false
+ return try {
+ val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
+ launcherApps.getLauncherUserInfo(userHandle)?.userType == "android.os.usertype.profile.PRIVATE"
+ } catch (e: Exception) {
+ false
+ }
+}
+
+fun isPrivateSpaceLocked(context: Context, userHandle: UserHandle): Boolean {
+ return try {
+ val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
+ userManager.isQuietModeEnabled(userHandle)
+ } catch (e: Exception) {
+ true
+ }
+}
+
+fun getPrivateSpaceUserHandle(context: Context): UserHandle? {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) return null
+ val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
+ for (profile in userManager.userProfiles) {
+ if (isPrivateSpaceProfile(context, profile)) return profile
+ }
+ return null
+}
+
+suspend fun getPrivateSpaceApps(
+ context: Context,
+ prefs: Prefs,
+): MutableList<AppModel> {
+ return withContext(Dispatchers.IO) {
+ val appList: MutableList<AppModel> = mutableListOf()
+ try {
+ val privateSpaceHandle = getPrivateSpaceUserHandle(context) ?: return@withContext appList
+ val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
+ val collator = Collator.getInstance()
+
+ for (app in launcherApps.getActivityList(null, privateSpaceHandle)) {
+ if (app.applicationInfo.packageName == BuildConfig.APPLICATION_ID) continue
+ val appLabelShown = prefs.getAppRenameLabel(app.applicationInfo.packageName)
+ .ifBlank { app.label.toString() }
+ appList.add(
+ AppModel.App(
+ appLabel = appLabelShown,
+ key = collator.getCollationKey(app.label.toString()),
+ appPackage = app.applicationInfo.packageName,
+ activityClassName = app.componentName.className,
+ isNew = false,
+ user = privateSpaceHandle
+ )
+ )
+ }
+ appList.sortWith(compareBy(collator) { it.appLabel })
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ appList
+ }
+}
+
fun getUserHandleFromString(context: Context, userHandleString: String): UserHandle {
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
for (userHandle in userManager.userProfiles) {
diff --git a/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt b/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt
index fd1d58e..01234a5 100644
--- a/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt
+++ b/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt
@@ -19,6 +19,7 @@ import app.olauncher.R
import app.olauncher.data.AppModel
import app.olauncher.data.Constants
import app.olauncher.databinding.AdapterAppDrawerBinding
+import app.olauncher.databinding.AdapterPrivateSpaceHeaderBinding
import app.olauncher.helper.hideKeyboard
import app.olauncher.helper.isSystemApp
import app.olauncher.helper.showKeyboard
@@ -32,9 +33,14 @@ class AppDrawerAdapter(
private val appDeleteListener: (AppModel) -> Unit,
private val appHideListener: (AppModel, Int) -> Unit,
private val appRenameListener: (AppModel, String) -> Unit,
-) : ListAdapter<AppModel, AppDrawerAdapter.ViewHolder>(DIFF_CALLBACK), Filterable {
+ private val privateSpaceToggleListener: () -> Unit = {},
+ private val privateSpaceSettingsListener: () -> Unit = {},
+) : ListAdapter<AppModel, RecyclerView.ViewHolder>(DIFF_CALLBACK), Filterable {
companion object {
+ const val VIEW_TYPE_APP = 0
+ const val VIEW_TYPE_PRIVATE_HEADER = 1
+
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<AppModel>() {
override fun areItemsTheSame(oldItem: AppModel, newItem: AppModel): Boolean = when {
oldItem is AppModel.App && newItem is AppModel.App ->
@@ -43,6 +49,8 @@ class AppDrawerAdapter(
oldItem is AppModel.PinnedShortcut && newItem is AppModel.PinnedShortcut ->
oldItem.shortcutId == newItem.shortcutId && oldItem.user == newItem.user
+ oldItem is AppModel.PrivateSpaceHeader && newItem is AppModel.PrivateSpaceHeader -> true
+
else -> false
}
@@ -59,30 +67,58 @@ class AppDrawerAdapter(
var appsList: MutableList<AppModel> = mutableListOf()
var appFilteredList: MutableList<AppModel> = mutableListOf()
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
- ViewHolder(
- AdapterAppDrawerBinding.inflate(
- LayoutInflater.from(parent.context),
- parent,
- false
+ override fun getItemViewType(position: Int): Int {
+ return when (appFilteredList.getOrNull(position)) {
+ is AppModel.PrivateSpaceHeader -> VIEW_TYPE_PRIVATE_HEADER
+ else -> VIEW_TYPE_APP
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ return when (viewType) {
+ VIEW_TYPE_PRIVATE_HEADER -> PrivateSpaceHeaderViewHolder(
+ AdapterPrivateSpaceHeaderBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
)
- )
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ else -> ViewHolder(
+ AdapterAppDrawerBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
try {
- if (appFilteredList.size == 0 || position == RecyclerView.NO_POSITION) return
+ if (appFilteredList.isEmpty() || position == RecyclerView.NO_POSITION) return
val appModel = appFilteredList[holder.bindingAdapterPosition]
- holder.bind(
- flag,
- appLabelGravity,
- myUserHandle,
- appModel,
- appClickListener,
- appDeleteListener,
- appInfoListener,
- appHideListener,
- appRenameListener
- )
+ when (holder) {
+ is PrivateSpaceHeaderViewHolder -> {
+ holder.bind(
+ appLabelGravity,
+ privateSpaceToggleListener,
+ privateSpaceSettingsListener,
+ )
+ }
+
+ is ViewHolder -> holder.bind(
+ flag,
+ appLabelGravity,
+ myUserHandle,
+ appModel,
+ appClickListener,
+ appDeleteListener,
+ appInfoListener,
+ appHideListener,
+ appRenameListener
+ )
+ }
} catch (e: Exception) {
e.printStackTrace()
}
@@ -98,7 +134,7 @@ class AppDrawerAdapter(
val appFilteredList = (if (charSearch.isNullOrBlank()) appsList
else appsList.filter { app ->
- appLabelMatches(app.appLabel, charSearch)
+ app !is AppModel.PrivateSpaceHeader && appLabelMatches(app.appLabel, charSearch)
} as MutableList<AppModel>)
val filterResults = FilterResults()
@@ -125,7 +161,8 @@ class AppDrawerAdapter(
&& autoLaunch
&& isBangSearch.not()
&& flag == Constants.FLAG_LAUNCH_APP
- && appFilteredList.size > 0
+ && appFilteredList.isNotEmpty()
+ && appFilteredList[0] !is AppModel.PrivateSpaceHeader
) appClickListener(appFilteredList[0])
} catch (e: Exception) {
e.printStackTrace()
@@ -158,8 +195,24 @@ class AppDrawerAdapter(
}
fun launchFirstInList() {
- if (appFilteredList.size > 0)
- appClickListener(appFilteredList[0])
+ val first = appFilteredList.firstOrNull { it !is AppModel.PrivateSpaceHeader }
+ if (first != null) appClickListener(first)
+ }
+
+ class PrivateSpaceHeaderViewHolder(private val binding: AdapterPrivateSpaceHeaderBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(
+ appLabelGravity: Int,
+ toggleListener: () -> Unit,
+ settingsListener: () -> Unit,
+ ) = with(binding) {
+ privateSpaceTitle.gravity = appLabelGravity
+ privateSpaceTitle.setOnClickListener { toggleListener() }
+ privateSpaceTitle.setOnLongClickListener {
+ settingsListener()
+ true
+ }
+ }
}
class ViewHolder(private val binding: AdapterAppDrawerBinding) :
@@ -237,7 +290,7 @@ class AppDrawerAdapter(
s: CharSequence?,
start: Int,
count: Int,
- after: Int
+ after: Int,
) {
}
diff --git a/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt b/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt
index 7e4cd83..1602a2b 100644
--- a/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt
+++ b/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt
@@ -39,6 +39,10 @@ class AppDrawerFragment : Fragment() {
private var flag = Constants.FLAG_LAUNCH_APP
private var canRename = false
+ private var currentAppList: List<AppModel>? = null
+ private var currentPrivateSpaceApps: List<AppModel>? = null
+ private var currentPrivateSpaceLocked: Boolean = true
+ private var currentPrivateSpaceAvailable: Boolean = false
private val viewModel: MainViewModel by activityViewModels()
private var _binding: FragmentAppDrawerBinding? = null
@@ -128,6 +132,7 @@ class AppDrawerFragment : Fragment() {
},
appDeleteListener = { appModel ->
when (appModel) {
+ is AppModel.PrivateSpaceHeader -> {}
is AppModel.PinnedShortcut ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
requireContext().deletePinnedShortcut(
@@ -181,9 +186,17 @@ class AppDrawerFragment : Fragment() {
val identifier = when (appModel) {
is AppModel.PinnedShortcut -> appModel.shortcutId
is AppModel.App -> appModel.appPackage
+ else -> return@AppDrawerAdapter
}
prefs.setAppRenameLabel(identifier, renameLabel)
viewModel.getAppList()
+ },
+ privateSpaceToggleListener = {
+ viewModel.togglePrivateSpaceLock()
+ },
+ privateSpaceSettingsListener = {
+ viewModel.openPrivateSpaceSettings()
+ findNavController().popBackStack(R.id.mainFragment, false)
}
)
@@ -221,14 +234,41 @@ class AppDrawerFragment : Fragment() {
}
} else {
viewModel.appList.observe(viewLifecycleOwner) {
- it?.let { appModels ->
- adapter.setAppList(appModels.toMutableList())
- adapter.filter.filter(binding.search.query)
+ currentAppList = it
+ updateCombinedAppList()
+ }
+ if (flag == Constants.FLAG_LAUNCH_APP) {
+ viewModel.privateSpaceAvailable.observe(viewLifecycleOwner) {
+ currentPrivateSpaceAvailable = it
+ updateCombinedAppList()
+ }
+ viewModel.privateSpaceLocked.observe(viewLifecycleOwner) {
+ currentPrivateSpaceLocked = it
+ updateCombinedAppList()
+ }
+ viewModel.privateSpaceApps.observe(viewLifecycleOwner) {
+ currentPrivateSpaceApps = it
+ updateCombinedAppList()
}
}
}
}
+ private fun updateCombinedAppList() {
+ val apps = currentAppList ?: return
+ val combined = apps.toMutableList()
+
+ if (flag == Constants.FLAG_LAUNCH_APP && currentPrivateSpaceAvailable) {
+ combined.add(AppModel.PrivateSpaceHeader(isLocked = currentPrivateSpaceLocked))
+ if (!currentPrivateSpaceLocked) {
+ currentPrivateSpaceApps?.let { combined.addAll(it) }
+ }
+ }
+
+ adapter.setAppList(combined)
+ adapter.filter.filter(binding.search.query)
+ }
+
private fun initClickListeners() {
binding.appRename.setOnClickListener {
val name = binding.search.query.toString().trim()
diff --git a/app/src/main/res/layout/adapter_private_space_header.xml b/app/src/main/res/layout/adapter_private_space_header.xml
new file mode 100644
index 0000000..992ca2c
--- /dev/null
+++ b/app/src/main/res/layout/adapter_private_space_header.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/privateSpaceTitle"
+ style="@style/TextLarge"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:alpha="0.8"
+ android:maxLines="1"
+ android:paddingHorizontal="20dp"
+ android:paddingVertical="@dimen/app_padding_vertical"
+ android:text="@string/private_space" />
+
+</LinearLayout>
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 803af99..7c3b953 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -163,4 +163,5 @@
<string name="new_year_wish_1">It doesn\'t matter how January went. You still have the rest of the year to do better than ever.</string>
<string name="cheers">Cheers!</string>
<string name="notification_bar">Notification bar</string>
+ <string name="private_space">Private space</string>
</resources> \ No newline at end of file