Initial commit

This commit is contained in:
MM20
2021-09-18 23:37:52 +02:00
commit 749e4e3073
938 changed files with 50475 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
{
"project_info": {
"project_number": "1080818269583",
"firebase_url": "https://quaesitio-1523049861646.firebaseio.com",
"project_id": "quaesitio-1523049861646",
"storage_bucket": "quaesitio-1523049861646.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:1080818269583:android:c37c9bbefdd27e04",
"android_client_info": {
"package_name": "de.mm20.launcher2.debug"
}
},
"oauth_client": [
{
"client_id": "1080818269583-br291p34djunaf9katvhomj5u5qcvr8c.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "de.mm20.launcher2.debug",
"certificate_hash": "af1d5f4a72fbaf9fce328142d1ed4a3ea4e77574"
}
},
{
"client_id": "1080818269583-4nll59hn87841bivhi9actpijmvpn6r0.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "1080818269583-36kh12cv92opc17pa49umltcih0qfh2q.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyDmEgLMLFnwYQviEE05mCg8kD6ik0gcWw4"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "1080818269583-4nll59hn87841bivhi9actpijmvpn6r0.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -0,0 +1,22 @@
package de.mm20.launcher2.debug
import android.os.StrictMode
import android.util.Log
import de.mm20.launcher2.BuildConfig
class Debug {
init {
Log.d("MM20", "MM20Launcher2 is running in debug mode.")
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
companion object {
const val DEBUG_MODE = true
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+3
View File
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Kvæsitso Debug</string>
</resources>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="debug">true</bool>
</resources>
+3
View File
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Kvæsitso Debug</string>
</resources>
+84
View File
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="de.mm20.launcher2">
<uses-permission android:name="android.permission.SET_ALARM" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name=".LauncherApplication"
android:allowBackup="true"
android:crossProfile="true"
android:fullBackupContent="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".activity.SettingsActivity"
android:label="@string/title_activity_settings"
android:launchMode="singleTask"
android:exported="true"
android:parentActivityName=".ui.legacy.activity.LauncherActivity"
android:screenOrientation="portrait"
android:taskAffinity="de.mm20.launcher2.settings"
android:theme="@style/SettingsTheme">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="de.mm20.launcher2.ui.legacy.activity.LauncherActivity" />
</activity>
<activity
android:name=".activity.AddItemActivity"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<provider
android:name=".content.GenericFileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

@@ -0,0 +1,84 @@
package de.mm20.launcher2
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import androidx.appcompat.app.AppCompatDelegate
import de.mm20.launcher2.debug.Debug
import de.mm20.launcher2.icons.IconRepository
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.preferences.Themes
import de.mm20.launcher2.ui.legacy.helper.WallpaperBlur
import kotlinx.coroutines.*
import java.text.Collator
import kotlin.coroutines.CoroutineContext
class LauncherApplication : Application(), CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + SupervisorJob()
var blurredWallpaper: Bitmap? = null
private val appReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
IconRepository.getInstance(this@LauncherApplication).requestIconPackListUpdate()
}
}
override fun onCreate() {
super.onCreate()
Debug()
instance = this
LauncherPreferences.initialize(this)
IconRepository.getInstance(this).requestIconPackListUpdate()
registerReceiver(appReceiver, IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_REPLACED)
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addAction(Intent.ACTION_MY_PACKAGE_REPLACED)
addAction(Intent.ACTION_PACKAGE_CHANGED)
addDataScheme("package")
})
val theme = LauncherPreferences.instance.theme
AppCompatDelegate.setDefaultNightMode(
when (theme) {
Themes.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO // light
Themes.DARK -> AppCompatDelegate.MODE_NIGHT_YES // dark, black
Themes.AUTO -> AppCompatDelegate.MODE_NIGHT_AUTO // auto
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM //system
}
)
WallpaperBlur.requestBlur(this)
@Suppress("DEPRECATION") // We need to access the wallpaper directly to blur it
registerReceiver(WallpaperReceiver(), IntentFilter(Intent.ACTION_WALLPAPER_CHANGED))
}
companion object {
lateinit var instance: LauncherApplication
val collator: Collator by lazy {
Collator.getInstance().apply { strength = Collator.SECONDARY }
}
}
}
object PermissionRequests {
const val CALENDAR = 309
const val LOCATION = 410
const val ALL = 666
}
class WallpaperReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
WallpaperBlur.requestBlur(context)
}
}
@@ -0,0 +1,28 @@
package de.mm20.launcher2.activity;
import android.app.Activity
import android.content.Context
import android.content.pm.LauncherApps
import android.os.Build
import android.os.Bundle
import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.search.data.AppShortcut
class AddItemActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val launcherApps = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val pinRequest = launcherApps.getPinItemRequest(intent) ?: return run { finish() }
val shortcutInfo = pinRequest.shortcutInfo ?: return run { finish() }
val shortcut = AppShortcut(this.applicationContext, shortcutInfo,
packageManager.getApplicationInfo(shortcutInfo.`package`, 0)
.loadLabel(packageManager).toString())
if (pinRequest.accept()) {
FavoritesRepository.getInstance(this).pinItem(shortcut)
}
}
finish()
}
}
@@ -0,0 +1,86 @@
package de.mm20.launcher2.activity
import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import de.mm20.launcher2.R
import de.mm20.launcher2.fragment.PreferencesCalendarFragment
import de.mm20.launcher2.fragment.PreferencesMainFragment
import de.mm20.launcher2.fragment.PreferencesServicesFragment
import de.mm20.launcher2.fragment.PreferencesWeatherFragment
import de.mm20.launcher2.ui.legacy.activity.LauncherActivity
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
val fragment = getStartFragment()
setupActionBar()
supportFragmentManager
.beginTransaction()
.add(android.R.id.content, fragment)
.commit()
} else if (!savedInstanceState.getBoolean("theme_change")) {
val fragment = getStartFragment()
setupActionBar()
supportFragmentManager
.beginTransaction()
.replace(android.R.id.content, fragment)
.commit()
}
findViewById<View>(android.R.id.content)?.setBackgroundColor(getColor(R.color.settings_window_background))
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean("theme_change", true)
}
private fun getStartFragment(): Fragment {
return when (intent.extras?.getString(FRAGMENT, "")) {
FRAGMENT_CALENDAR -> PreferencesCalendarFragment()
FRAGMENT_WEATHER -> PreferencesWeatherFragment()
FRAGMENT_SERVICES -> PreferencesServicesFragment()
else -> PreferencesMainFragment()
}
}
private fun setupActionBar() {
actionBar?.setDisplayHomeAsUpEnabled(true)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home) {
if (supportFragmentManager.backStackEntryCount == 0) {
finish()
startActivity(Intent(this, LauncherActivity::class.java))
} else {
supportFragmentManager.popBackStack()
}
return true
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
finish()
startActivity(Intent(this, LauncherActivity::class.java))
}
}
companion object {
const val RESULT_NEED_RESTART = 0x09
const val FRAGMENT_WEATHER: String = "weather"
const val FRAGMENT_CALENDAR: String = "calendar"
const val FRAGMENT_SERVICES: String = "services"
const val FRAGMENT: String = "fragment"
}
}
@@ -0,0 +1,5 @@
package de.mm20.launcher2.content
import androidx.core.content.FileProvider
class GenericFileProvider: FileProvider()
@@ -0,0 +1,176 @@
package de.mm20.launcher2.fragment
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import com.afollestad.materialdialogs.MaterialDialog
import de.mm20.launcher2.R
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.helper.DebugInformationDumper
class PreferencesAboutFragment : PreferenceFragmentCompat() {
private var easterEggCounter = 0
@SuppressLint("ResourceType")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_about)
val versionPref = findPreference<Preference>("version")!!
try {
val version = requireContext().packageManager.getPackageInfo(
requireActivity().application.packageName,
0
).versionName
versionPref.summary = version
} catch (e: PackageManager.NameNotFoundException) {
//Should never happen
versionPref.summary = "Ich mag Bockwurst-Bananen"
}
versionPref.setOnPreferenceClickListener {
if (easterEggCounter in arrayOf(3, 4, 7)) Toast.makeText(
context, when (easterEggCounter) {
3 -> R.string.easter_egg_1
4 -> R.string.easter_egg_2
7 -> R.string.easter_egg_3
else -> 0
}, Toast.LENGTH_SHORT
).show()
if (easterEggCounter == 8) {
easterEggCounter = 0
requireFragmentManager().beginTransaction()
.setCustomAnimations(
R.anim.preference_fragment_child_enter,
R.anim.preference_fragment_parent_exit,
R.anim.preference_fragment_parent_enter,
R.anim.preference_fragment_child_exit
)
.replace(
android.R.id.content,
PreferencesEasterEggFragment()
)
.addToBackStack(null)
.commit()
}
easterEggCounter++
false
}
val licenses = findPreference<Preference>("category_licenses") as PreferenceCategory
for (l in LICENSES) {
val license = resources.obtainTypedArray(l)
val preference = Preference(activity, null, 0, R.style.Preference_Material)
preference.title = license.getString(0)
preference.summary = license.getString(1)
preference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
parentFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.preference_fragment_child_enter,
R.anim.preference_fragment_parent_exit,
R.anim.preference_fragment_parent_enter,
R.anim.preference_fragment_child_exit
)
.replace(android.R.id.content,
PreferencesLicenseFragment().apply { library = l })
.addToBackStack(null)
.commit()
true
}
license.recycle()
licenses.addPreference(preference)
}
findPreference<Preference>("crash_reporter")?.setOnPreferenceClickListener {
startActivity(CrashReporter.getLaunchIntent())
true
}
findPreference<Preference>("export_debug")?.setOnPreferenceClickListener {
Toast.makeText(
activity,
getString(
R.string.debug_export_information_file,
DebugInformationDumper().dump(requireContext())
),
Toast.LENGTH_SHORT
).show()
true
}
findPreference<Preference>("export_databases")?.setOnPreferenceClickListener {
MaterialDialog(requireContext()).show {
message(res = R.string.debug_export_databases_warning)
positiveButton(res = R.string.dialog_continue, click = {
Toast.makeText(
activity,
getString(
R.string.debug_export_information_file,
DebugInformationDumper().exportDatabases(requireContext())
),
Toast.LENGTH_SHORT
).show()
it.dismiss()
})
negativeButton(res = android.R.string.cancel, click = {
it.cancel()
})
}
true
}
findPreference<Preference>("license")?.setOnPreferenceClickListener {
parentFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.preference_fragment_child_enter,
R.anim.preference_fragment_parent_exit,
R.anim.preference_fragment_parent_enter,
R.anim.preference_fragment_child_exit
)
.replace(android.R.id.content,
PreferencesLicenseFragment().apply { library = R.array.license_mm20launcher2 })
.addToBackStack(null)
.commit()
true
}
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_about)
}
companion object {
private val LICENSES = intArrayOf(
R.array.license_accompanist,
R.array.license_android_jetpack,
R.array.license_suncalc,
R.array.license_crashreporter,
R.array.license_draglinearlayout,
R.array.license_glide,
R.array.license_glide_transformations,
R.array.license_google_apiclient,
R.array.license_google_auth,
R.array.license_groupie,
R.array.license_gson,
R.array.license_jsoup,
R.array.license_kotlin_stdlib,
R.array.license_lottie,
R.array.license_mdicons,
R.array.license_material_components,
R.array.license_materialdialogs,
R.array.license_msal,
R.array.license_msgraph,
R.array.license_mxparser,
R.array.license_okhttp,
R.array.license_retrofit,
R.array.license_textdrawable,
R.array.license_viewpropertyobjectanimator
)
}
}
@@ -0,0 +1,182 @@
package de.mm20.launcher2.fragment
import android.Manifest
import android.app.WallpaperManager
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat
import androidx.lifecycle.lifecycleScope
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import de.mm20.launcher2.R
import de.mm20.launcher2.icons.IconPackManager
import de.mm20.launcher2.icons.IconRepository
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.checkPermission
import de.mm20.launcher2.preferences.IconShape
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.preferences.Themes
import de.mm20.launcher2.ui.legacy.view.LauncherIconView
import kotlinx.coroutines.launch
class PreferencesAppearanceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_appearance)
findPreference<Preference>("theme")?.setOnPreferenceChangeListener { _, newValue ->
val theme = Themes.byValue(newValue as String)
@Suppress("DEPRECATION") // Still using MODE_NIGHT_AUTO
AppCompatDelegate.setDefaultNightMode(when (theme) {
Themes.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
Themes.DARK -> AppCompatDelegate.MODE_NIGHT_YES
Themes.AUTO -> AppCompatDelegate.MODE_NIGHT_AUTO
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
})
requireActivity().recreate()
true
}
if (WallpaperManager.getInstance(activity).wallpaperInfo != null) {
findPreference<Preference>("blur_cards")?.apply {
setSummary(R.string.preference_blur_cards_summary_lwp)
isEnabled = false
}
}
findPreference<Preference>("blur_cards")?.setOnPreferenceChangeListener { _, newValue ->
val newVal = newValue as? Boolean ?: return@setOnPreferenceChangeListener true
if (newVal && requireActivity().checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 0)
}
true
}
findPreference<Preference>("wallpaper")?.setOnPreferenceClickListener {
requireContext().startActivity(Intent.createChooser(Intent(Intent.ACTION_SET_WALLPAPER), null))
true
}
findPreference<Preference>("cards")?.setOnPreferenceClickListener {
requireFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.preference_fragment_child_enter, R.anim.preference_fragment_parent_exit,
R.anim.preference_fragment_parent_enter, R.anim.preference_fragment_child_exit)
.replace(android.R.id.content, PreferencesCardFragment())
.addToBackStack(null)
.commit()
true
}
val manager = IconPackManager.getInstance(requireContext())
lifecycleScope.launch {
val packs = manager.getInstalledIconPacks()
findPreference<ListPreference>("icon_pack")?.apply {
entries = packs.map { it.name }.toMutableList().apply { add(0, "System") }.toTypedArray()
entryValues = (-1 until packs.size).map { it.toString() }.toTypedArray()
if (packs.isEmpty()) {
isEnabled = false
setSummary(R.string.preference_icon_pack_summary_empty)
} else {
isEnabled = true
summary = "%s"
value = packs.indexOfFirst { it.packageName == manager.selectedIconPack }.toString()
}
setOnPreferenceChangeListener { _, newValue ->
val index = (newValue as String).toInt()
IconRepository.getInstance(requireContext()).clearCache()
if (index == -1) manager.selectIconPack("")
else {
manager.selectIconPack(packs[index].packageName)
}
true
}
}
}
findPreference<Preference>("legacy_icon_bg")?.setOnPreferenceChangeListener { _, _ ->
IconRepository.getInstance(requireContext()).clearCache()
true
}
val shapePreference = findPreference<Preference>("icon_shape")!!
shapePreference.summary = getShapeName()
shapePreference.setOnPreferenceClickListener {
val launcherIcon = LauncherIcon(
foreground = requireContext().getDrawable(R.mipmap.ic_launcher_foreground)!!,
background = requireContext().getDrawable(R.mipmap.ic_launcher_background)
)
val iconShapeList = LinearLayout(requireContext())
iconShapeList.orientation = LinearLayout.VERTICAL
val shapes = arrayOf(
IconShape.PLATFORM_DEFAULT to R.string.preference_icon_shape_platform,
IconShape.CIRCLE to R.string.preference_icon_shape_circle,
IconShape.ROUNDED_SQUARE to R.string.preference_icon_shape_rounded_square,
IconShape.SQUARE to R.string.preference_icon_shape_square,
IconShape.SQUIRCLE to R.string.preference_icon_shape_squircle,
IconShape.HEXAGON to R.string.preference_icon_shape_hexagon,
IconShape.TRIANGLE to R.string.preference_icon_shape_triangle,
IconShape.PENTAGON to R.string.preference_icon_shape_pentagon
)
val layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT)
val dialog = MaterialDialog(requireContext())
shapes.forEachIndexed { i, shape ->
val view = View.inflate(requireContext(), R.layout.preference_icon_shape_row, null)
view.findViewById<LauncherIconView>(R.id.icon).also { iconView ->
iconView.icon = launcherIcon
iconView.shape = shape.first
}
view.findViewById<TextView>(R.id.label).also { labelView ->
labelView.setText(shape.second)
}
view.layoutParams = layoutParams
iconShapeList.addView(view)
view.setOnClickListener {
LauncherPreferences.instance.iconShape = shape.first
shapePreference.summary = getShapeName()
dialog.dismiss()
}
}
dialog.customView(view = iconShapeList, scrollable = true)
.title(R.string.preference_icon_shape)
.negativeButton(android.R.string.cancel) {
dialog.cancel()
}
.show()
true
}
val systemBarsCategory = findPreference<PreferenceCategory>("system_bars")!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
systemBarsCategory.removePreference(findPreference("light_nav_bar"))
}
}
private fun getShapeName(): String {
return requireContext().getString(when (LauncherIconView.getDefaultShape(requireContext())) {
IconShape.TRIANGLE -> R.string.preference_icon_shape_triangle
IconShape.HEXAGON -> R.string.preference_icon_shape_hexagon
IconShape.ROUNDED_SQUARE -> R.string.preference_icon_shape_rounded_square
IconShape.SQUIRCLE -> R.string.preference_icon_shape_squircle
IconShape.SQUARE -> R.string.preference_icon_shape_square
IconShape.PENTAGON -> R.string.preference_icon_shape_pentagon
IconShape.PLATFORM_DEFAULT -> R.string.preference_icon_shape_platform
else -> R.string.preference_icon_shape_circle
})
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar
?.setTitle(R.string.preference_screen_appearance)
}
}
@@ -0,0 +1,47 @@
package de.mm20.launcher2.fragment
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import de.mm20.launcher2.R
import de.mm20.launcher2.badges.BadgeProvider
import de.mm20.launcher2.notifications.NotificationService
class PreferencesBadgesFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_badges)
findPreference<Preference>("notification_badges")?.setOnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean) {
de.mm20.launcher2.notifications.NotificationService.getInstance()?.generateBadges()
} else {
BadgeProvider.getInstance(requireContext()).removeNotificationBadges()
}
true
}
findPreference<Preference>("suspended_badges")?.setOnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean) {
BadgeProvider.getInstance(requireContext()).addSuspendBadges()
} else {
BadgeProvider.getInstance(requireContext()).removeSuspendBadges()
}
true
}
findPreference<Preference>("cloud_badges")?.setOnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean) {
BadgeProvider.getInstance(requireContext()).addCloudBadges()
} else {
BadgeProvider.getInstance(requireContext()).removeCloudBadges()
}
true
}
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar
?.setTitle(R.string.preference_screen_badges)
}
}
@@ -0,0 +1,121 @@
package de.mm20.launcher2.fragment
import android.Manifest
import android.content.res.ColorStateList
import android.os.Bundle
import android.widget.CheckBox
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.setPadding
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.customview.customView
import de.mm20.launcher2.R
import de.mm20.launcher2.ktx.checkPermission
import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.search.data.CalendarEvent
import de.mm20.launcher2.search.data.UserCalendar
class PreferencesCalendarFragment : PreferenceFragmentCompat() {
private var hasCalendarPermission = false
private val calendars = mutableListOf<UserCalendar>()
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_calendar)
init()
}
fun init(requestPermission: Boolean = true) {
val context = context ?: return
hasCalendarPermission = context.checkPermission(Manifest.permission.READ_CALENDAR)
if (hasCalendarPermission) {
calendars.clear()
calendars.addAll(CalendarEvent.getCalendars(context))
val unselectedCalendars = LauncherPreferences.instance.unselectedCalendars.toMutableList()
findPreference<Preference>("calendar_calendars")?.apply {
var count = calendars.size - unselectedCalendars.size
summary = resources.getQuantityString(R.plurals.preference_calendar_calendars_summary, count, count)
isEnabled = true
setOnPreferenceClickListener {
val sheetView = LinearLayout(activity)
sheetView.setPadding((8 * context.dp).toInt())
sheetView.orientation = LinearLayout.VERTICAL
sheetView.setBackgroundColor(ContextCompat.getColor(context, R.color.bottom_sheet))
var owner = ""
val padding = (8 * context.dp).toInt()
for (c in calendars) {
if (owner != c.owner) {
owner = c.owner
val text = TextView(activity)
text.setTextColor(ContextCompat.getColor(context, R.color.text_color_secondary_normal))
text.setPadding(padding, 2 * padding, padding, padding)
text.text = owner
sheetView.addView(text)
}
val checkbox = CheckBox(activity)
checkbox.text = c.name
checkbox.buttonTintList = ColorStateList.valueOf(CalendarEvent.getDisplayColor(context, c.color))
checkbox.setPadding(padding)
checkbox.isChecked = !unselectedCalendars.contains(c.id)
checkbox.setOnCheckedChangeListener { _, checked ->
if (checked) {
unselectedCalendars.remove(c.id)
} else {
unselectedCalendars.add(c.id)
}
LauncherPreferences.instance.unselectedCalendars = unselectedCalendars
count = calendars.size - unselectedCalendars.size
summary = resources.getQuantityString(R.plurals.preference_calendar_calendars_summary, count, count)
}
sheetView.addView(checkbox)
}
val scrollView = ScrollView(context)
scrollView.isNestedScrollingEnabled = true
scrollView.addView(sheetView)
MaterialDialog(context, BottomSheet()).show {
customView(view = scrollView)
title(R.string.preference_calendar_calendars)
.negativeButton(R.string.close) {
dismiss()
}
}
true
}
}
} else {
if (requestPermission) {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR),
0)
}
findPreference<Preference>("calendar_calendars")?.apply {
isEnabled = false
setSummary(R.string.preference_permission_denied)
}
}
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_calendar)
hasCalendarPermission = requireActivity().checkPermission(Manifest.permission.READ_CALENDAR)
&& requireActivity().checkPermission(Manifest.permission.WRITE_CALENDAR)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
init(false)
}
}
@@ -0,0 +1,199 @@
package de.mm20.launcher2.fragment
import android.animation.Animator
import android.animation.ObjectAnimator
import android.app.WallpaperManager
import android.graphics.*
import android.os.Bundle
import android.view.View
import android.view.ViewOutlineProvider
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.doOnNextLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import de.mm20.launcher2.LauncherApplication
import de.mm20.launcher2.R
import de.mm20.launcher2.ktx.castTo
import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.ktx.translate
import de.mm20.launcher2.preferences.CardBackground
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.ui.legacy.helper.WallpaperBlur
import kotlinx.android.synthetic.main.fragment_card_settings.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.math.roundToInt
class PreferencesCardFragment : Fragment(R.layout.fragment_card_settings) {
val preferences = LauncherPreferences.instance
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
previewCard.strokeOpacity = 0xFF
previewCardBlur.clipToOutline = true
previewCardBlur.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline?) {
val radius = preferences.cardRadius
outline?.setRoundRect(0, 0, view.width, view.height, radius * dp)
}
}
val context = requireContext()
val previewCardBlur = previewCardBlur
val previewCard = previewCard
val prefFragment = PreferenesCardInnerFragment()
prefFragment.onPreferencesReady = {
findPreference<Preference>("card_radius")?.let {
it.summary = preferences.cardRadius.toString()
it.setOnPreferenceChangeListener { pref, newValue ->
val value = newValue as Int
previewCard.radius = value * dp
previewCardBlur.invalidateOutline()
pref.summary = value.toString()
true
}
}
findPreference<Preference>("card_opacity")?.let {
it.summary = preferences.cardOpacity.toString()
it.setOnPreferenceChangeListener { pref, newValue ->
val value = newValue as Int
previewCard.backgroundOpacity = value
previewCard.cardElevation = if (value == 0xFF) resources.getDimension(R.dimen.card_elevation) else 0f
pref.summary = value.toString()
true
}
}
findPreference<Preference>("card_stroke_width")?.let {
it.summary = preferences.cardRadius.toString()
it.setOnPreferenceChangeListener { pref, newValue ->
val value = newValue as Int
previewCard.strokeWidth = (value * dp).roundToInt()
pref.summary = value.toString()
true
}
}
findPreference<Preference>("blur_cards")?.let {
if (WallpaperManager.getInstance(requireContext()).wallpaperInfo != null) {
it.isEnabled = false
it.setSummary(R.string.preference_blur_cards_summary_lwp)
previewCardBlur.visibility = View.INVISIBLE
} else {
previewCardBlur.visibility = if (preferences.blurCards) {
View.VISIBLE
} else {
View.INVISIBLE
}
it.setOnPreferenceChangeListener { pref, newValue ->
previewCardBlur.visibility = if (newValue as Boolean) {
View.VISIBLE
} else {
View.INVISIBLE
}
true
}
}
}
findPreference<Preference>("card_background")?.let {
it.setOnPreferenceChangeListener { preference, newValue ->
val background = CardBackground.byValue(newValue as String)
var color = when (background) {
CardBackground.BLACK -> context.getColor(R.color.cardview_background_black)
else -> context.getColor(R.color.cardview_background)
}
color = color and ((previewCard.backgroundOpacity shl 24) or 0xFFFFFF)
previewCard.setCardBackgroundColor(color)
true
}
}
}
childFragmentManager.beginTransaction()
.replace(R.id.preferencesView, prefFragment)
.commit()
}
private var blurBitmap: Bitmap? = null
private var animator: Animator? = null
override fun onStart() {
super.onStart()
val content = activity?.findViewById<View>(android.R.id.content) ?: return
animator = ObjectAnimator.ofArgb(content, "backgroundColor", ResourcesCompat.getColor(resources, R.color.settings_window_background, null), Color.TRANSPARENT)
.apply {
duration = 200
startDelay = resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
start()
}
if (preferences.blurCards && preferences.cardOpacity < 0xFF) {
lifecycleScope.launch {
val wallpaper = withContext(Dispatchers.IO) {
WallpaperBlur.getCachedBitmap(requireContext())
}
LauncherApplication.instance.blurredWallpaper = wallpaper
}
}
content.doOnNextLayout {
WallpaperManager.getInstance(requireContext()).setWallpaperOffsets(it.windowToken, 0.5f, 0.5f)
}
val activity = requireActivity()
lifecycleScope.launch {
val viewPosition = intArrayOf(0, 0)
val rect = Rect(0, 0, previewCardBlur.width, previewCardBlur.height)
val screen = Point()
activity.windowManager.defaultDisplay.getRealSize(screen)
previewCardBlur.getLocationOnScreen(viewPosition)
val file = File(requireContext().cacheDir, "wallpaper")
if (!file.exists()) return@launch
blurBitmap = withContext(Dispatchers.IO) {
val wallpaperWidth: Int
val wallpaperHeight: Int
val decoder = BitmapRegionDecoder
.newInstance(file.absolutePath, false)
wallpaperHeight = decoder.height
wallpaperWidth = decoder.width
if (wallpaperWidth >= screen.x && wallpaperHeight >= screen.y) {
val translateX = (wallpaperWidth - previewCardBlur.width) / 2f
val translateY = (wallpaperHeight - screen.y) / 2f + viewPosition[1]
rect.translate(translateX.roundToInt(),
translateY.roundToInt())
}
decoder.decodeRegion(rect, null)
}
previewCardBlur.setImageBitmap(blurBitmap)
}
}
override fun onStop() {
super.onStop()
if (animator?.isRunning == true) animator?.end()
activity?.findViewById<View>(android.R.id.content)?.setBackgroundColor(ResourcesCompat.getColor(resources, R.color.settings_window_background, null))
}
}
class PreferenesCardInnerFragment : PreferenceFragmentCompat() {
var onPreferencesReady: (PreferenesCardInnerFragment.() -> Unit)? = null
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_cards)
onPreferencesReady?.invoke(this)
}
}
@@ -0,0 +1,29 @@
package de.mm20.launcher2.fragment
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import android.widget.ToggleButton
import androidx.fragment.app.Fragment
import de.mm20.launcher2.R
import de.mm20.launcher2.preferences.LauncherPreferences
class PreferencesEasterEggFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_easteregg, null, false)
val root = view.findViewById<FrameLayout>(R.id.easterEggRoot)
val toggle = view.findViewById<ToggleButton>(R.id.magicModeToggle)
toggle.isChecked = LauncherPreferences.instance.easterEggEnabled
toggle.setOnCheckedChangeListener { _, isChecked ->
LauncherPreferences.instance.easterEggEnabled = isChecked
Toast.makeText(requireContext(), if (isChecked) R.string.easter_egg_activated else R.string.easter_egg_deactivated, Toast.LENGTH_SHORT).show()
}
return view
}
}
@@ -0,0 +1,60 @@
package de.mm20.launcher2.fragment
import android.annotation.SuppressLint
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import de.mm20.launcher2.R
class PreferencesLicenseFragment : Fragment() {
var library: Int = 0
@SuppressLint("ResourceType")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_license, null, false)
val license = resources.obtainTypedArray(library)
(activity as AppCompatActivity).supportActionBar?.title = license.getString(0)
val icon = view.findViewById<ImageView>(R.id.icon)
val iconUri = license.getString(2)
if (iconUri == null) icon.visibility = View.GONE
else {
Glide
.with(icon)
.load(iconUri)
.into(icon)
icon.visibility = View.VISIBLE
}
val url = license.getString(6)
val website = view.findViewById<TextView>(R.id.website)
website.setOnClickListener {
val intent = CustomTabsIntent.Builder()
.setDefaultColorSchemeParams(CustomTabColorSchemeParams
.Builder()
.setToolbarColor(-0x9f8275)
.build())
.setShowTitle(true)
.build()
intent.launchUrl(activity as AppCompatActivity, Uri.parse(url))
}
val description = view.findViewById<TextView>(R.id.description)
description.text = license.getString(1)
val licenseTitle = view.findViewById<TextView>(R.id.licenseTitle)
val licenseText = view.findViewById<TextView>(R.id.licenseText)
val licenseCopyright = view.findViewById<TextView>(R.id.licenseCopyright)
licenseTitle.text = license.getString(3)
licenseCopyright.text = license.getString(4)
licenseText.text = resources.openRawResource(license.getResourceId(5, 0)).reader().readText()
license.recycle()
return view
}
}
@@ -0,0 +1,60 @@
package de.mm20.launcher2.fragment
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import de.mm20.launcher2.R
class PreferencesMainFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_main)
findPreference<Preference>("screen_appearance")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesAppearanceFragment())
true
}
findPreference<Preference>("screen_about")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesAboutFragment())
true
}
findPreference<Preference>("screen_weather")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesWeatherFragment())
true
}
findPreference<Preference>("screen_services")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesServicesFragment())
true
}
findPreference<Preference>("screen_search")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesSearchFragment())
true
}
findPreference<Preference>("screen_calendar")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesCalendarFragment())
true
}
findPreference<Preference>("screen_badges")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesBadgesFragment())
true
}
}
private fun setSettingsScreen(fragment: Fragment) {
parentFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.preference_fragment_child_enter, R.anim.preference_fragment_parent_exit,
R.anim.preference_fragment_parent_enter, R.anim.preference_fragment_child_exit)
.replace(android.R.id.content, fragment)
.addToBackStack(null)
.commit()
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.title_activity_settings)
}
}
@@ -0,0 +1,176 @@
package de.mm20.launcher2.fragment
import android.Manifest
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import de.mm20.launcher2.R
import de.mm20.launcher2.gservices.GoogleApiHelper
import de.mm20.launcher2.ktx.checkPermission
import de.mm20.launcher2.msservices.MicrosoftGraphApiHelper
import de.mm20.launcher2.nextcloud.NextcloudApiHelper
import de.mm20.launcher2.owncloud.OwncloudClient
import de.mm20.launcher2.preferences.LauncherPreferences
import kotlinx.coroutines.launch
class PreferencesSearchFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
updateGoogleDrive()
updateOneDrive()
}
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_search)
findPreference<Preference>("search_activities")?.summary =
getString(
R.string.preference_search_activities_summary,
requireActivity().componentName.flattenToShortString()
)
findPreference<Preference>("search_files")?.setOnPreferenceChangeListener { _, newValue ->
if (newValue == true &&
requireContext().checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
) {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
0
)
}
true
}
findPreference<Preference>("search_edit_websearch")?.setOnPreferenceClickListener {
setSettingsScreen(PreferencesWebSearchesFragment())
true
}
}
private suspend fun updateGoogleDrive() {
val googleApiHelper = GoogleApiHelper.getInstance(context ?: return)
val account = googleApiHelper.getAccount()
val pref = findPreference<Preference>("search_gdrive")!!
if (account == null) {
pref.apply {
setSummary(R.string.preference_summary_not_logged_in)
}
} else {
pref.apply {
summary = context.getString(R.string.preference_search_gdrive_summary, account.name)
}
}
val isSignedIn = account != null
pref.setOnPreferenceChangeListener { _, value ->
val newVal = value as Boolean
if (newVal && !isSignedIn) {
googleLogin()
}
true
}
}
private suspend fun updateOneDrive() {
val oneDrivePref = findPreference<Preference>("search_onedrive")!!
val user = MicrosoftGraphApiHelper.getInstance(requireContext()).getUser()
if (user == null) {
oneDrivePref.setSummary(R.string.preference_summary_not_logged_in)
oneDrivePref.setOnPreferenceChangeListener { _, value ->
if (value as Boolean) {
lifecycleScope.launch launch2@{
MicrosoftGraphApiHelper.getInstance(requireContext())
.login(requireActivity())
updateOneDrive()
}
}
true
}
} else {
oneDrivePref.summary =
context?.getString(R.string.preference_search_onedrive_summary, user.name)
}
}
private fun updateNextcloud() {
val nextcloudPref = findPreference<Preference>("search_nextcloud")!!
val client = NextcloudApiHelper(context ?: return)
lifecycleScope.launch {
val user = client.getLoggedInUser()
if (user == null) {
nextcloudPref.setSummary(R.string.preference_summary_not_logged_in)
LauncherPreferences.instance.searchNextcloud = false
nextcloudPref.setOnPreferenceChangeListener { _, value ->
if (value as Boolean) {
lifecycleScope.launch launch2@{
updateNextcloud()
}
}
true
}
} else {
nextcloudPref.summary = context?.getString(
R.string.preference_search_nextcloud_summary,
user.displayName
)
}
}
}
private fun updateOwncloud() {
val owncloudPref = findPreference<Preference>("search_owncloud")!!
lifecycleScope.launch {
val client = OwncloudClient(context ?: return@launch)
val user = client.getLoggedInUser()
if (user == null) {
owncloudPref.setSummary(R.string.preference_summary_not_logged_in)
LauncherPreferences.instance.searchOwncloud = false
owncloudPref.setOnPreferenceChangeListener { _, value ->
if (value as Boolean) {
lifecycleScope.launch launch2@{
client.login(requireActivity(), 0)
updateOwncloud()
}
}
true
}
} else {
owncloudPref.summary = context?.getString(
R.string.preference_search_nextcloud_summary,
user.displayName,
)
}
}
}
private fun googleLogin() {
GoogleApiHelper.getInstance(requireContext()).login(requireActivity())
}
private fun setSettingsScreen(fragment: Fragment) {
parentFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.preference_fragment_child_enter, R.anim.preference_fragment_parent_exit,
R.anim.preference_fragment_parent_enter, R.anim.preference_fragment_child_exit
)
.replace(android.R.id.content, fragment)
.addToBackStack(null)
.commit()
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_search)
updateNextcloud()
updateOwncloud()
}
}
@@ -0,0 +1,174 @@
package de.mm20.launcher2.fragment
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import de.mm20.launcher2.R
import de.mm20.launcher2.gservices.GoogleApiHelper
import de.mm20.launcher2.msservices.MicrosoftGraphApiHelper
import de.mm20.launcher2.nextcloud.NextcloudApiHelper
import de.mm20.launcher2.owncloud.OwncloudClient
import kotlinx.coroutines.launch
class PreferencesServicesFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
updateGooglePreferences()
updateMicrosoftPreferences()
updateNextcloudPreferences()
}
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_services)
}
private suspend fun updateGooglePreferences() {
val pref = findPreference<Preference>("google_signin")!!
val googleApiHelper = GoogleApiHelper.getInstance(requireContext())
if (!googleApiHelper.isAvailable()) {
pref.isEnabled = false
pref.summary = context?.getString(R.string.feature_not_available, context?.getString(R.string.app_name))
return
}
val account = googleApiHelper.getAccount()
if (account == null) {
pref.apply {
setTitle(R.string.preference_google_signin)
setSummary(R.string.preference_google_signin_summary)
setOnPreferenceClickListener {
googleApiHelper.login(requireActivity())
true
}
}
} else {
pref.apply {
title = context.getString(R.string.preference_signin_logout)
summary = context.getString(R.string.preference_signin_user, account.name)
setOnPreferenceClickListener {
googleApiHelper.logout()
lifecycleScope.launch {
updateGooglePreferences()
}
true
}
}
}
}
private suspend fun updateMicrosoftPreferences() {
val pref = findPreference<Preference>("ms_signin")!!
val msApiHelper = MicrosoftGraphApiHelper.getInstance(requireContext())
if (!msApiHelper.isAvailable()) {
pref.isEnabled = false
pref.summary = context?.getString(R.string.feature_not_available, context?.getString(R.string.app_name))
return
}
val user = MicrosoftGraphApiHelper.getInstance(requireContext()).getUser()
if (user == null) {
pref.setTitle(R.string.preference_ms_signin)
pref.setSummary(R.string.preference_ms_signin_summary)
pref.setOnPreferenceClickListener {
lifecycleScope.launch {
msApiHelper.login(requireActivity())
updateMicrosoftPreferences()
}
true
}
} else {
pref.setTitle(R.string.preference_signin_logout)
pref.summary = context?.getString(R.string.preference_signin_user, user.name)
pref.setOnPreferenceClickListener {
lifecycleScope.launch {
msApiHelper.logout()
updateMicrosoftPreferences()
}
true
}
}
}
private suspend fun updateNextcloudPreferences() {
val nextcloud = NextcloudApiHelper(requireContext())
val user = nextcloud.getLoggedInUser()
if (user == null) {
findPreference<Preference>("nextcloud_signin")?.let {
it.setOnPreferenceClickListener {
nextcloud.login(requireActivity())
true
}
it.setTitle(R.string.preference_nextcloud_signin)
it.setSummary(R.string.preference_nextcloud_signin_summary)
}
} else {
findPreference<Preference>("nextcloud_signin")?.let {
it.setOnPreferenceClickListener {
lifecycleScope.launch {
nextcloud.logout()
updateNextcloudPreferences()
}
true
}
it.setTitle(R.string.preference_signin_logout)
it.summary = context?.getString(
R.string.preference_signin_user_nextcloud,
user.displayName
)
}
}
}
private fun updateOwncloudPreferences() {
val client = OwncloudClient(context ?: return)
lifecycleScope.launch {
val user = client.getLoggedInUser()
if (user == null) {
findPreference<Preference>("owncloud_signin")?.let {
it.setOnPreferenceClickListener {
OwncloudClient(requireContext()).login(
requireActivity(),
REQUEST_OWNCLOUD_LOGIN
)
true
}
it.setTitle(R.string.preference_owncloud_signin)
it.setSummary(R.string.preference_owncloud_signin_summary)
}
} else {
findPreference<Preference>("owncloud_signin")?.let {
it.setOnPreferenceClickListener {
OwncloudClient(requireContext()).logout()
updateOwncloudPreferences()
true
}
it.setTitle(R.string.preference_signin_logout)
it.summary = context?.getString(
R.string.preference_signin_user_nextcloud,
user.displayName,
)
}
}
}
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_services)
updateOwncloudPreferences()
}
companion object {
const val REQUEST_OWNCLOUD_LOGIN = 581
}
}
@@ -0,0 +1,166 @@
package de.mm20.launcher2.fragment
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItems
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import de.mm20.launcher2.R
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.preferences.WeatherProviders
import de.mm20.launcher2.weather.WeatherProvider
import de.mm20.launcher2.weather.WeatherViewModel
import de.mm20.launcher2.weather.here.HereProvider
import de.mm20.launcher2.weather.metno.MetNoProvider
import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class PreferencesWeatherFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences_weather)
findPreference<Preference>("location")?.setOnPreferenceChangeListener { _, newValue ->
lifecycleScope.launch {
val locations = withContext(Dispatchers.IO) {
WeatherProvider.getInstance(requireContext())
?.lookupLocation(newValue as String)
} ?: return@launch
onLookupCompleted(locations)
}
false
}
/*findPreference<Preference>("weather_provider")?.setOnPreferenceChangeListener { pref, newValue ->
val newProvider = WeatherProviders.byValue(newValue as String)
LauncherPreferences.instance.weatherProvider = newProvider
WeatherProvider.getInstance(requireContext())?.resetLastUpdate()
ViewModelProvider(this).get(WeatherViewModel::class.java).requestUpdate(requireContext())
updateProviderPreferences()
true
}*/
val providerPref = findPreference<Preference>("weather_provider")!!
val context = requireContext()
val providers = mutableListOf<Pair<WeatherProviders, String>>()
OpenWeatherMapProvider(context).takeIf { it.isAvailable() }?.let {
providers.add(WeatherProviders.OPENWEATHERMAP to it.name)
}
HereProvider(context).takeIf { it.isAvailable() }?.let {
providers.add(WeatherProviders.HERE to it.name)
}
MetNoProvider(context).takeIf { it.isAvailable() }?.let {
providers.add(WeatherProviders.MET_NO to it.name)
}
if (providers.isEmpty()) {
providerPref.summary = context.getString(
R.string.feature_not_available,
context.getString(R.string.app_name)
)
providerPref.isEnabled = false
} else {
providerPref.setOnPreferenceClickListener {
MaterialDialog(context).show {
title(R.string.preference_weather_provider)
listItemsSingleChoice(
items = providers.map { it.second },
initialSelection = providers.indexOfFirst { it.first == LauncherPreferences.instance.weatherProvider }
) { dialog, index, text ->
LauncherPreferences.instance.weatherProvider = providers[index].first
WeatherProvider.getInstance(requireContext())?.resetLastUpdate()
ViewModelProvider(this@PreferencesWeatherFragment)
.get(WeatherViewModel::class.java)
.requestUpdate(requireContext())
updateProviderPreferences()
dialog.dismiss()
}
}
true
}
}
findPreference<Preference>("auto_location")?.setOnPreferenceChangeListener { _, newValue ->
val autoLocation = newValue as Boolean
val provider = WeatherProvider.getInstance(requireContext())
provider?.autoLocation = autoLocation
provider?.resetLastUpdate()
provider?.setLocation(null, "")
ViewModelProvider(this).get(WeatherViewModel::class.java)
.requestUpdate(requireContext())
updateProviderPreferences()
true
}
updateProviderPreferences()
}
private fun updateProviderPreferences() {
val provider = WeatherProvider.getInstance(requireContext())
val autoLocationPref = findPreference<SwitchPreference>("auto_location")!!
val locationPref = findPreference<Preference>("location")!!
val unitsPref = findPreference<Preference>("imperial_units")!!
val providerPref = findPreference<Preference>("weather_provider")!!
locationPref.parent?.isVisible = provider != null
unitsPref.isVisible = provider != null
provider ?: return
providerPref.summary = provider.name
if (provider.supportsAutoLocation) {
autoLocationPref.setSummary(R.string.preference_automatic_location_summary)
autoLocationPref.isChecked = provider.autoLocation
if (!provider.supportsManualLocation) {
autoLocationPref.isEnabled = false
autoLocationPref.isChecked = true
locationPref.isEnabled = false
locationPref.setSummary(R.string.preference_location_disabled_summary)
} else {
autoLocationPref.isEnabled = true
autoLocationPref.isChecked = provider.autoLocation
locationPref.isEnabled = true
locationPref.summary = provider.getLastLocation()
}
} else {
autoLocationPref.isEnabled = false
autoLocationPref.setSummary(R.string.preference_automatic_location_disabled_summary)
autoLocationPref.isChecked = false
}
}
private fun onLookupCompleted(results: List<Pair<Any?, String>>) {
MaterialDialog(requireContext())
.listItems(
items = results.map { it.second },
waitForPositiveButton = false
) { dialog, index, _ ->
val provider = WeatherProvider.getInstance(requireContext())
?: return@listItems dialog.dismiss()
provider.resetLastUpdate()
provider.setLocation(results[index].first, results[index].second)
findPreference<Preference>("location")?.summary = results[index].second
ViewModelProvider(this).get(WeatherViewModel::class.java)
.requestUpdate(requireContext())
dialog.dismiss()
}
.negativeButton {
it.cancel()
}
.show()
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.preference_screen_weather)
}
}
@@ -0,0 +1,229 @@
package de.mm20.launcher2.fragment
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.scale
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.color.colorChooser
import com.afollestad.materialdialogs.customview.customView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import de.mm20.launcher2.R
import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.search.SearchViewModel
import de.mm20.launcher2.search.WebsearchViewModel
import de.mm20.launcher2.search.data.Websearch
import java.io.File
import java.io.FileOutputStream
import java.lang.ref.WeakReference
class PreferencesWebSearchesFragment : PreferenceFragmentCompat() {
private lateinit var rootView: View
private var sheetIcon: WeakReference<ImageView>? = null
private val viewModel by lazy {
ViewModelProvider(context as AppCompatActivity)[WebsearchViewModel::class.java]
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceScreen = preferenceManager.createPreferenceScreen(activity)
val searches = viewModel.allWebsearches
searches.observe(context as AppCompatActivity, Observer {
updatePreferenceScreen(it)
})
}
private fun updatePreferenceScreen(searches: List<Websearch>) {
preferenceScreen.removeAll()
for (search in searches) {
val pref = Preference(context)
pref.title = search.label
if (search.icon == null) {
val drawable = resources.getDrawable(R.drawable.ic_search, requireActivity().theme).mutate()
drawable.setTintMode(PorterDuff.Mode.SRC_ATOP)
drawable.setTint(search.color)
pref.icon = drawable
} else {
Glide.with(requireContext())
.asDrawable()
.load(search.icon)
.into(object : SimpleTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
pref.icon = resource
}
})
}
pref.setOnPreferenceClickListener {
editSearch(search)
true
}
preferenceScreen.addPreference(pref)
}
val newPref = Preference(activity)
newPref.setTitle(R.string.preference_websearch_new)
newPref.setIcon(R.drawable.ic_preference_websearch_new)
newPref.setOnPreferenceClickListener {
editSearch(null)
true
}
preferenceScreen.addPreference(newPref)
}
private fun editSearch(search: Websearch?) {
val websearch = search ?: Websearch("", "", 0xFF555555.toInt(), null, null)
val dialogView = LayoutInflater.from(activity).inflate(R.layout.dialog_websearch, null)
val nameEdit = dialogView.findViewById<EditText>(R.id.websearchName)
nameEdit.setText(websearch.label)
val urlEdit = dialogView.findViewById<EditText>(R.id.websearchUrl)
urlEdit.setText(websearch.urlTemplate)
val iconView = dialogView.findViewById<ImageView>(R.id.websearchIcon)
iconView.apply {
if (websearch.icon == null) {
setImageResource(R.drawable.ic_search)
imageTintList = ColorStateList.valueOf(websearch.color)
} else {
Glide.with(this)
.load(websearch.icon)
.into(this)
}
sheetIcon = WeakReference(this)
}
val sheet = MaterialDialog(requireContext(), BottomSheet())
.cornerRadius(8f)
.customView(view = dialogView)
val radius = 8 * dialogView.dp
dialogView.background = GradientDrawable().apply {
cornerRadii = floatArrayOf(
radius, radius, // top left
radius, radius, // top right
0f, 0f, // bottom left
0f, 0f // bottom right
)
}
var newColor = websearch.color
var newIcon: String? = websearch.icon
sheet.noAutoDismiss()
.positiveButton(android.R.string.ok) {
val newUrl = urlEdit.text.toString()
val newName = nameEdit.text.toString()
if (!newUrl.contains("\${1}")) {
urlEdit.error = getString(R.string.websearch_dialog_url_error)
return@positiveButton
}
File(requireContext().cacheDir, "websearch-tmp").takeIf { it.exists() }?.let {
websearch.icon?.let { File(it).takeIf { it.exists() }?.delete() }
val newFile = File(requireContext().filesDir, "websearch-${System.currentTimeMillis()}")
it.copyTo(newFile, true)
it.delete()
newIcon = newFile.absolutePath
}
if (newIcon == null) {
websearch.icon?.let { File(it).takeIf { it.exists() }?.delete() }
}
websearch.urlTemplate = newUrl
websearch.label = newName
websearch.icon = newIcon
websearch.color = newColor
viewModel.insertWebsearch(websearch)
sheet.dismiss()
}
sheet.negativeButton(android.R.string.cancel) {
sheet.cancel()
}
@Suppress("DEPRECATION")
sheet.neutralButton(R.string.menu_delete) {
sheet.dismiss()
websearch.icon?.let { File(it).takeIf { it.exists() }?.delete() }
viewModel.deleteWebsearch(websearch)
}
sheet.setOnCancelListener {
File(requireContext().cacheDir, "websearch-tmp").takeIf { it.exists() }?.delete()
}
dialogView.findViewById<View>(R.id.websearchIcon).setOnClickListener {
MaterialDialog(requireContext()).show {
@Suppress("DEPRECATION")
neutralButton(R.string.custom_icon) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
try {
startActivityForResult(intent, 24)
} catch (e: ActivityNotFoundException) {
}
dismiss()
}
title(R.string.websearch_dialog_choose_icon_color)
colorChooser(
colors = context.resources.getIntArray(R.array.color_chooser_presets),
allowCustomArgb = true,
showAlphaSelector = false
) { _, color ->
iconView.setImageResource(R.drawable.ic_search)
iconView.imageTintList = ColorStateList.valueOf(color)
newColor = color
newIcon = null
File(requireContext().cacheDir, "websearch-tmp").takeIf { it.exists() }?.delete()
dismiss()
}
}
}
sheet.show()
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar
?.setTitle(R.string.preference_search_edit_websearch)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val dataUri = data?.data
if (requestCode == 24 && resultCode == Activity.RESULT_OK && dataUri != null) {
val stream = requireActivity().contentResolver.openInputStream(dataUri)
val icon = BitmapFactory.decodeStream(stream)
val scaledIcon = icon.scale((32 * requireContext().dp).toInt(), (32 * requireContext().dp).toInt())
val out = FileOutputStream(File(requireContext().cacheDir, "websearch-tmp"))
scaledIcon.compress(Bitmap.CompressFormat.PNG, 100, out)
out.close()
sheetIcon?.get()?.apply {
imageTintList = null
setImageBitmap(scaledIcon)
}
}
}
}
@@ -0,0 +1,45 @@
package de.mm20.launcher2.helper
import android.content.Context
import android.os.Build
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
class DebugInformationDumper {
fun dump(context: Context): String {
val df = SimpleDateFormat("yyyy-MM-dd-HHmmss")
val file = File(context.getExternalFilesDir(null), "kvaesitso-log-${df.format(Date(System.currentTimeMillis()))}")
val fos = file.outputStream().writer()
fos.write("Device: ${Build.DEVICE}\n")
fos.write("SDK version: ${Build.VERSION.SDK_INT}\n")
fos.write("====================================\n")
Thread {
val input = Runtime.getRuntime().exec("/system/bin/sh -c logcat").inputStream.bufferedReader()
var line = input.readLine()
while (line != null) {
line = input.readLine()
fos.write("$line\n")
}
fos.close()
}.start()
return file.absolutePath
}
fun exportDatabases(context: Context): String {
val df = SimpleDateFormat("yyyy-MM-dd-HHmmss")
val exportFile = File(context.getExternalFilesDir(null), "room-${df.format(Date(System.currentTimeMillis()))}.db")
GlobalScope.launch {
withContext(Dispatchers.IO) {
context.getDatabasePath("room").copyTo(exportFile)
}
}
return exportFile.absolutePath
}
}
@@ -0,0 +1,99 @@
package de.mm20.launcher2.ui.preferences
import android.animation.Animator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.postDelayed
import androidx.preference.Preference
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.airbnb.lottie.LottieAnimationView
import de.mm20.launcher2.R
import de.mm20.launcher2.preferences.AppStartAnimation
import de.mm20.launcher2.preferences.LauncherPreferences
class AppStartAnimPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.preferenceStyle) : Preference(context, attrs, defStyleAttr) {
init {
summary = getNameForAnimation(LauncherPreferences.instance.appStartAnim)
setOnPreferenceClickListener {
val anims = mutableListOf(
AppStartAnimation.M to R.raw.app_start_anim_m,
AppStartAnimation.SLIDE_BOTTOM to R.raw.app_start_anim_slide_bottom,
AppStartAnimation.FADE to R.raw.app_start_anim_fade
)
val dialog = MaterialDialog(context)
val layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val list = LinearLayout(context)
list.orientation = LinearLayout.VERTICAL
anims.forEachIndexed { _, anim ->
val view = View.inflate(context, R.layout.preference_start_anim_item, null)
view.findViewById<LottieAnimationView>(R.id.icon).also { iconView ->
iconView.setAnimation(anim.second)
iconView.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator) {
iconView.postDelayed(500) {
iconView.frame = 0
}
iconView.postDelayed(1300) {
iconView.playAnimation()
}
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
iconView.postDelayed(300) {
iconView.playAnimation()
}
}
view.findViewById<TextView>(R.id.label).also { labelView ->
labelView.setText(getNameForAnimation(anim.first))
}
view.layoutParams = layoutParams
list.addView(view)
view.setOnClickListener {
LauncherPreferences.instance.appStartAnim = anim.first
summary = getNameForAnimation(anim.first)
dialog.dismiss()
}
}
dialog.customView(view = list, scrollable = true)
.title(R.string.preference_app_start_animation)
.negativeButton(android.R.string.cancel) {
dialog.cancel()
}
.show()
true
}
}
private fun getNameForAnimation(anim: AppStartAnimation): String {
return when (anim) {
AppStartAnimation.FADE -> context.getString(R.string.preference_app_start_animation_fade)
AppStartAnimation.SLIDE_BOTTOM -> context.getString(R.string.preference_app_start_animation_slide_bottom)
AppStartAnimation.M -> context.getString(R.string.preference_app_start_animation_m)
else -> context.getString(R.string.preference_app_start_animation_default)
}
}
}
@@ -0,0 +1,73 @@
package de.mm20.launcher2.ui.view
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import androidx.annotation.XmlRes
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import de.mm20.launcher2.R
import de.mm20.launcher2.ktx.castTo
class PreferencesView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val fragment = PreferenceViewFragment()
init {
if (id == View.NO_ID) id = View.generateViewId()
context.castTo<AppCompatActivity>().supportFragmentManager.beginTransaction()
.add(id, fragment)
.commit()
attrs?.let {
val ta = context.theme.obtainStyledAttributes(it, R.styleable.PreferencesView, 0, defStyleAttr)
val preferenceScreen = ta.getResourceId(R.styleable.SearchGridView_columnCount, 0)
setPreferenceResource(preferenceScreen)
ta.recycle()
}
}
fun setPreferenceResource(@XmlRes resId: Int) {
if (resId == 0) return
fragment.setPreferenceResource(resId)
}
fun <T : Preference> findPreference(key: String): T? {
return fragment.findPreference<T>(key)
}
var onPreferencesReady: (() -> Unit)? = null
set(value) {
field = value
fragment.onPreferencesReady = value
}
}
class PreferenceViewFragment : PreferenceFragmentCompat() {
private var isInitialized = false
var onPreferencesReady: (() -> Unit)? = null
@XmlRes
private var preferenceResource = 0
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
if (preferenceResource != 0) addPreferencesFromResource(preferenceResource)
isInitialized = true
onPreferencesReady?.invoke()
onPreferencesReady = null
}
internal fun setPreferenceResource(@XmlRes resId: Int) {
preferenceResource = resId
if (isInitialized && resId != 0) {
addPreferencesFromResource(resId)
}
}
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
<translate
android:fromYDelta="-30%"
android:toYDelta="0"/>
</set>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime">
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"/>
<translate
android:fromYDelta="0"
android:toYDelta="30%"/>
</set>
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="pathData"
android:valueFrom="M14,19 18,19 18,5 14,5Z M6,19 10,19 10,5 6,5Z"
android:valueTo="M12,16.8 19,16.8 12,5.8 12,5.8Z M5,16.8 12,16.8 12,5.8 12,5.8Z"
android:valueType="pathType"/>
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="90" />
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="pathData"
android:valueFrom="M12,16.8 19,16.8 12,5.8 12,5.8Z M5,16.8 12,16.8 12,5.8 12,5.8Z"
android:valueTo="M14,19 18,19 18,5 14,5Z M6,19 10,19 10,5 6,5Z"
android:valueType="pathType"/>
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="rotation"
android:valueFrom="90"
android:valueTo="180" />
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="pathData"
android:valueFrom="M 14.5,12 6,18 V 6 L14.5,12 Z"
android:valueTo="M18,18 16,18 V 6L 18,6Z"
android:valueType="pathType"/>
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="pathData"
android:valueFrom="M 6,12 L 6,12 L 6,12 V 12 Z"
android:valueTo="M6,18L14.5,12L6,6V18Z"
android:valueType="pathType"/>
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="pathData"
android:valueFrom="M9.5,12L18,6V18L9.5,12Z"
android:valueTo="M6,6L6,8V18L6,18Z"
android:valueType="pathType"/>
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="pathData"
android:valueFrom="M18,12L18,12V12L18,12Z"
android:valueTo="M9.5,12L18,6V18L9.5,12Z"
android:valueType="pathType"/>
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@android:integer/config_shortAnimTime"
android:ordering="together">
<translate
android:fromXDelta="100%"
android:toXDelta="0%" />
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@android:integer/config_shortAnimTime"
android:ordering="together">
<translate
android:fromXDelta="0%"
android:toXDelta="100%" />
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@android:integer/config_shortAnimTime"
android:ordering="together">
<translate
android:fromXDelta="-100%"
android:toXDelta="0%" />
</set>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@android:integer/config_shortAnimTime"
android:ordering="together">
<translate
android:fromXDelta="0%"
android:toXDelta="-100%" />
</set>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_enabled="true"
android:state_pressed="true">
<set>
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="7dp"
android:valueType="floatType"/>
</set>
</item>
<item>
<set>
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="0"
android:valueType="floatType"/>
</set>
</item>
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

File diff suppressed because one or more lines are too long
@@ -0,0 +1,19 @@
<vector android:height="24dp" android:viewportHeight="48.000004"
android:viewportWidth="48" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.05" android:fillColor="#fff"
android:pathData="M8,4L40,4A4,4 0,0 1,44 8L44,40A4,4 0,0 1,40 44L8,44A4,4 0,0 1,4 40L4,8A4,4 0,0 1,8 4z"
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="3.77952766"/>
<path android:fillAlpha="1" android:fillColor="#5C6BC0"
android:pathData="M8,4C5.784,4 4,5.784 4,8L4,14L44,14L44,8C44,5.784 42.216,4 40,4L8,4z"
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.1748693"/>
<path android:fillAlpha="1" android:fillColor="#fff"
android:fillType="evenOdd"
android:pathData="m24,31.8506a1.4002,1.4002 0,0 0,1.4002 -1.4002c0,-0.7771 -0.6301,-1.4002 -1.4002,-1.4002a1.4002,1.4002 0,0 0,-1.4002 1.4002,1.4002 1.4002,0 0,0 1.4002,1.4002m4.2007,-6.301a1.4002,1.4002 0,0 1,1.4002 1.4002l0,7.0011a1.4002,1.4002 0,0 1,-1.4002 1.4002L19.7993,35.3512A1.4002,1.4002 0,0 1,18.3991 33.9509L18.3991,26.9498C18.3991,26.1727 19.0292,25.5496 19.7993,25.5496L20.4994,25.5496L20.4994,24.1494a3.5006,3.5006 0,0 1,3.5006 -3.5006,3.5006 3.5006,0 0,1 3.5006,3.5006l0,1.4002l0.7001,0M24,22.0491A2.1003,2.1003 0,0 0,21.8997 24.1494l0,1.4002l4.2007,0L26.1003,24.1494A2.1003,2.1003 0,0 0,24 22.0491Z"
android:strokeLineCap="butt" android:strokeLineJoin="miter" android:strokeWidth="1"/>
<path android:fillAlpha="1" android:fillColor="#ffffff"
android:pathData="M11,9m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="3.77952766"/>
<path android:fillAlpha="1" android:fillColor="#ffffff"
android:pathData="M37,9m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="3.77952766"/>
</vector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<group
android:pivotX="16"
android:pivotY="16"
android:scaleX="0.75"
android:scaleY="0.75">
<path
android:fillColor="?android:colorAccent"
android:pathData="M 4.4570312 3.6269531 A 0.44109249 0.44109249 0 0 0 4.1757812 3.71875 A 0.44109249 0.44109249 0 0 0 4.0957031 4.3378906 L 5.3691406 5.984375 C 5.3240789 6.102208 5.2949219 6.2272726 5.2949219 6.3613281 L 5.2949219 6.5507812 L 5.2949219 7.4199219 L 5.2949219 7.7714844 L 5.2949219 8.640625 L 5.2949219 8.8300781 C 5.2949219 9.4165531 5.7670378 9.8886719 6.3535156 9.8886719 L 17.646484 9.8886719 C 18.232962 9.8886719 18.705078 9.4165531 18.705078 8.8300781 L 18.705078 8.640625 L 18.705078 6.5507812 L 18.705078 6.3613281 C 18.705078 6.2272726 18.675921 6.102208 18.630859 5.984375 L 19.904297 4.3378906 A 0.44109249 0.44109249 0 0 0 19.824219 3.71875 A 0.44109249 0.44109249 0 0 0 19.566406 3.6269531 A 0.44109249 0.44109249 0 0 0 19.207031 3.796875 L 17.990234 5.3710938 C 17.881129 5.3332803 17.768779 5.3027344 17.646484 5.3027344 L 6.3535156 5.3027344 C 6.2312214 5.3027344 6.1188708 5.3332803 6.0097656 5.3710938 L 4.7929688 3.796875 A 0.44109249 0.44109249 0 0 0 4.4570312 3.6269531 z M 8.5664062 6.625 A 1.1909523 1.1909465 0 0 1 8.6035156 6.625 A 1.1909523 1.1909465 0 0 1 9.7949219 7.8164062 A 1.1909523 1.1909465 0 0 1 8.6035156 9.0078125 A 1.1909523 1.1909465 0 0 1 7.4121094 7.8164062 A 1.1909523 1.1909465 0 0 1 8.5664062 6.625 z M 15.447266 6.625 A 1.1909523 1.1909465 0 0 1 15.484375 6.625 A 1.1909523 1.1909465 0 0 1 16.675781 7.8164062 A 1.1909523 1.1909465 0 0 1 15.484375 9.0078125 A 1.1909523 1.1909465 0 0 1 14.292969 7.8164062 A 1.1909523 1.1909465 0 0 1 15.447266 6.625 z " />
<path
android:fillColor="?android:colorAccent"
android:pathData="M 6.3535157,10.242188 C 5.7670379,10.242188 5.294922,10.714306 5.294922,11.300781 V 11.572266 13.771484 15.888672 18.087891 18.359375 C 5.294922,18.94585 5.7670379,19.417969 6.3535157,19.417969 H 17.646484 C 18.232962,19.417969 18.705078,18.94585 18.705078,18.359375 V 18.087891 11.572266 11.300781 C 18.705078,10.714306 18.232962,10.242188 17.646484,10.242188 Z" />
</group>
</vector>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<group
android:pivotX="16"
android:pivotY="16"
android:scaleX="0.75"
android:scaleY="0.75">
<path
android:fillColor="?android:colorAccent"
android:pathData="M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z" />
</group>
</vector>
@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<group
android:pivotX="16"
android:pivotY="16"
android:scaleX="0.75"
android:scaleY="0.75">
<path
android:fillColor="?android:colorAccent"
android:pathData="M9.78,18.65L10.06,14.42L17.74,7.5C18.08,7.19 17.67,7.04 17.22,7.31L7.74,13.3L3.64,12C2.76,11.75 2.75,11.14 3.84,10.7L19.81,4.54C20.54,4.21 21.24,4.72 20.96,5.84L18.24,18.65C18.05,19.56 17.5,19.78 16.74,19.36L12.6,16.3L10.61,18.23C10.38,18.46 10.19,18.65 9.78,18.65Z" />
</group>
</vector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?android:colorAccent" android:pathData="M-0.05,16.79L3.19,12.97L-0.05,9.15L1.5,7.86L4.5,11.41L7.5,7.86L9.05,9.15L5.81,12.97L9.05,16.79L7.5,18.07L4.5,14.5L1.5,18.07L-0.05,16.79M24,17A1,1 0 0,1 23,18H20A2,2 0 0,1 18,16V14A2,2 0 0,1 20,12H22V10H18V8H23A1,1 0 0,1 24,9M22,14H20V16H22V14M16,17A1,1 0 0,1 15,18H12A2,2 0 0,1 10,16V10A2,2 0 0,1 12,8H14V5H16V17M14,16V10H12V16H14Z" />
</vector>
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M23.52,12.2727C23.52,11.4218 23.4436,10.6036 23.3018,9.8182L12,9.8182L12,14.46L18.4582,14.46C18.18,15.96 17.3345,17.2309 16.0636,18.0818L16.0636,21.0927L19.9418,21.0927C22.2109,19.0036 23.52,15.9273 23.52,12.2727Z"
android:strokeWidth="1"
android:fillColor="#4285f4"
android:strokeColor="#00000000"
android:fillType="evenOdd"/>
<path
android:pathData="M12,24C15.24,24 17.9564,22.9255 19.9418,21.0927L16.0636,18.0818C14.9891,18.8018 13.6145,19.2273 12,19.2273 8.8745,19.2273 6.2291,17.1164 5.2855,14.28L1.2764,14.28L1.2764,17.3891C3.2509,21.3109 7.3091,24 12,24Z"
android:strokeWidth="1"
android:fillColor="#34a853"
android:strokeColor="#00000000"
android:fillType="evenOdd"/>
<path
android:pathData="M5.2855,14.28C5.0455,13.56 4.9091,12.7909 4.9091,12 4.9091,11.2091 5.0455,10.44 5.2855,9.72L5.2855,6.6109L1.2764,6.6109C0.4636,8.2309 0,10.0636 0,12 0,13.9364 0.4636,15.7691 1.2764,17.3891Z"
android:strokeWidth="1"
android:fillColor="#fbbc05"
android:strokeColor="#00000000"
android:fillType="evenOdd"/>
<path
android:pathData="M12,4.7727C13.7618,4.7727 15.3436,5.3782 16.5873,6.5673L20.0291,3.1255C17.9509,1.1891 15.2345,0 12,0 7.3091,0 3.2509,2.6891 1.2764,6.6109L5.2855,9.72C6.2291,6.8836 8.8745,4.7727 12,4.7727Z"
android:strokeWidth="1"
android:fillColor="#ea4335"
android:strokeColor="#00000000"
android:fillType="evenOdd"/>
</vector>
@@ -0,0 +1,7 @@
<vector android:height="24dp" android:viewportHeight="21"
android:viewportWidth="21" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#f25022" android:pathData="M1,1h9v9h-9z"/>
<path android:fillColor="#00a4ef" android:pathData="M1,11h9v9h-9z"/>
<path android:fillColor="#7fba00" android:pathData="M11,1h9v9h-9z"/>
<path android:fillColor="#ffb900" android:pathData="M11,11h9v9h-9z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:viewportHeight="12"
android:viewportWidth="12" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="1" android:fillColor="#0082c9"
android:pathData="M6,3.4778C4.8506,3.4778 3.8844,4.2622 3.5841,5.3187 3.3227,4.7595 2.7616,4.3644 2.108,4.3644 1.2099,4.3644 0.4724,5.1019 0.4724,6 0.4724,6.8981 1.2099,7.6356 2.108,7.6356 2.7616,7.6356 3.3227,7.2405 3.5841,6.6813 3.8844,7.7378 4.8506,8.5222 6,8.5222 7.1494,8.5222 8.1156,7.7378 8.4159,6.6813 8.6773,7.2405 9.2384,7.6356 9.892,7.6356 10.7901,7.6356 11.5276,6.8981 11.5276,6 11.5276,5.1019 10.7901,4.3644 9.892,4.3644 9.2384,4.3644 8.6773,4.7595 8.4159,5.3187 8.1156,4.2622 7.1494,3.4778 6,3.4778ZM6,4.4514C6.8607,4.4514 7.5486,5.1393 7.5486,6 7.5486,6.8607 6.8607,7.5486 6,7.5486 5.1393,7.5486 4.4514,6.8607 4.4514,6 4.4514,5.1393 5.1393,4.4514 6,4.4514ZM2.108,5.3356C2.4801,5.3356 2.7723,5.6278 2.7723,6 2.7723,6.3722 2.4801,6.6644 2.108,6.6644 1.7358,6.6644 1.4436,6.3722 1.4436,6 1.4436,5.6278 1.7358,5.3356 2.108,5.3356ZM9.892,5.3356C10.2642,5.3356 10.5564,5.6278 10.5564,6 10.5564,6.3722 10.2642,6.6644 9.892,6.6644 9.5199,6.6644 9.2277,6.3722 9.2277,6 9.2277,5.6278 9.5199,5.3356 9.892,5.3356Z" android:strokeWidth="1.23695016"/>
</vector>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>
+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
</vector>
+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M14,17L4,17v2h10v-2zM20,9L4,9v2h16L20,9zM4,15h16v-2L4,13v2zM4,5v2h16L20,5L4,5z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,9H4v2h16V9zM4,15h16v-2H4v2z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M14,17H12V15H10V13H12V15H14M14,9H12V11H14V13H12V11H10V9H12V7H10V5H12V7H14M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M8,3A2,2 0 0,0 6,5V9A2,2 0 0,1 4,11H3V13H4A2,2 0 0,1 6,15V19A2,2 0 0,0 8,21H10V19H8V14A2,2 0 0,0 6,12A2,2 0 0,0 8,10V5H10V3M16,3A2,2 0 0,1 18,5V9A2,2 0 0,0 20,11H21V13H20A2,2 0 0,0 18,15V19A2,2 0 0,1 16,21H14V19H16V14A2,2 0 0,1 18,12A2,2 0 0,1 16,10V5H14V3H16Z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M14,17H7V15H14M17,13H7V11H17M17,9H7V7H17M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
</vector>
@@ -0,0 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FFF" android:pathData="M9,5V9H21V5M9,19H21V15H9M9,14H21V10H9M4,9H8V5H4M4,19H8V15H4M4,14H8V10H4V14Z" />
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M9.4,16.6L4.8,12l4.6,-4.6L8,6l-6,6 6,6 1.4,-1.4zM14.6,16.6l4.6,-4.6 -4.6,-4.6L16,6l6,6 -6,6 -1.4,-1.4z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M11.43,10.94C11.2,11.68 10.87,12.47 10.42,13.34C10.22,13.72 10,14.08 9.92,14.38L10.03,14.34V14.34C11.3,13.85 12.5,13.57 13.37,13.41C13.22,13.31 13.08,13.2 12.96,13.09C12.36,12.58 11.84,11.84 11.43,10.94M17.91,14.75C17.74,14.94 17.44,15.05 17,15.05C16.24,15.05 15,14.82 14,14.31C12.28,14.5 11,14.73 9.97,15.06C9.92,15.08 9.86,15.1 9.79,15.13C8.55,17.25 7.63,18.2 6.82,18.2C6.66,18.2 6.5,18.16 6.38,18.09L5.9,17.78L5.87,17.73C5.8,17.55 5.78,17.38 5.82,17.19C5.93,16.66 6.5,15.82 7.7,15.07C7.89,14.93 8.19,14.77 8.59,14.58C8.89,14.06 9.21,13.45 9.55,12.78C10.06,11.75 10.38,10.73 10.63,9.85V9.84C10.26,8.63 10.04,7.9 10.41,6.57C10.5,6.19 10.83,5.8 11.2,5.8H11.44C11.67,5.8 11.89,5.88 12.05,6.04C12.71,6.7 12.4,8.31 12.07,9.64C12.05,9.7 12.04,9.75 12.03,9.78C12.43,10.91 13,11.82 13.63,12.34C13.89,12.54 14.18,12.74 14.5,12.92C14.95,12.87 15.38,12.85 15.79,12.85C17.03,12.85 17.78,13.07 18.07,13.54C18.17,13.7 18.22,13.89 18.19,14.09C18.18,14.34 18.09,14.57 17.91,14.75M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M17.5,14.04C17.4,13.94 17,13.69 15.6,13.69C15.53,13.69 15.46,13.69 15.37,13.79C16.1,14.11 16.81,14.3 17.27,14.3C17.34,14.3 17.4,14.29 17.46,14.28H17.5C17.55,14.26 17.58,14.25 17.59,14.15C17.57,14.12 17.55,14.08 17.5,14.04M8.33,15.5C8.12,15.62 7.95,15.73 7.85,15.81C7.14,16.46 6.69,17.12 6.64,17.5C7.09,17.35 7.68,16.69 8.33,15.5M11.35,8.59L11.4,8.55C11.47,8.23 11.5,7.95 11.56,7.73L11.59,7.57C11.69,7 11.67,6.71 11.5,6.47L11.35,6.42C11.33,6.45 11.3,6.5 11.28,6.54C11.11,6.96 11.12,7.69 11.35,8.59Z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M19,16H5V8H19M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z"/>
</vector>

Some files were not shown because too many files have changed in this diff Show More