Initial commit
@@ -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"
|
||||
}
|
||||
|
After Width: | Height: | Size: 45 KiB |
|
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>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 923 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Kvæsitso Debug</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="debug">true</bool>
|
||||
</resources>
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Kvæsitso Debug</string>
|
||||
</resources>
|
||||
@@ -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>
|
||||
|
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>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 692 B |
@@ -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>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||