diff --git a/.gitignore b/.gitignore index 0db2eb33..cb0d9ed2 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ captures/ .idea/usage.statistics.xml .idea/contentModel.xml .idea/kotlinc.xml +.idea/jarRepositories.xml # optional .idea/dictionaries .idea/inspectionProfiles diff --git a/.travis.yml b/.travis.yml index c02ebbb6..794108a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,12 @@ language: java os: linux dist: bionic group: edge -jdk: openjdk10 git: depth: false env: global: - # for updates check developer.android.com/studio#downloads (current 26.1.1) - - ANDROID_SDK_TOOLS=sdk-tools-linux-4333796.zip + # for updates check (dl.google.com/android/repository/repository2-1.xml) + - ANDROID_SDK_CMD_TOOLS=commandlinetools-linux-5842447_latest.zip # storepass, keypass, keyalias - secure: K4PVOrcYr6ZV16GgZWcw5RGDqxRTrilMK6pjz1r+RkQX6EPywmTsUH0y8EGP44bZc+TFMM0UfGPulHfrKzC3LDVSe+CpvNdQqq8c2Ysc9lQLubvFmVlWZ2rjHhA3jtg33FDIAWipb41WZEdmCJE1EI+OMbh8p8/7cGZ4K4tpd3B8ViXrf16ht50C56glL1lS3Jog/g9OEIPdhzYF23nYPOAeV3xJg3WBGxUUMOwp3vrpMJ/bYvnh/XLHUpPWnCcSWhKZolE3C6Itlv3CUdCyd2u8dnbTFX7KK0g4nrJdXJAyith0aE2RB6APdDDIdZkF3p2qU3aWBWVvNzjGc6tYpP9OB8sjobcx9oG8lIaO09qZnM+fLTs5b3ulvl8d3UZI0KxgSocvjxpltrqeuNODGarzwIWAmjxKr3Qnfo5LFUna1UMxKJ1ARyT7zS9yUbfE6ek42aEe7rEaqryjNFE5X6b9D2WexN+68YynvdRfDXlFx2JIW3hWTi7AG4zBI0LKhhtruwLY1hKty3JR5/Dz/dIMW6JZUmLdE7drPmLNBcKRh1H778EOcaD+1q1bzVnwbA+HLfHkO9Rzmk7UOY1ttWzFBH23W/pI6D6mn4WTFng0/iOEsw7fwHaXPGv6ZXxserC/nzmeYb0AfK3wq2p2ztEDtbSblw9lkMBYlvNi5I8= - secure: SEcF7dl6ImTdeUYtw6dGeHRXbS4h8Ec9+Dnt2rFeobupo4e64818Fo10Uqqf+eM/5VVF2FAJLnEiq1SgfWZKjvUz9batJZNknc2JSKEGQPFaUD55USFCt2rxoLPFJKIee07kPTiXGPM2WWA+42cD+HpXAErMTd3BESsGwjni+xj2PhJuETDGrw+0D5T4TOXgd0uXNPl4p1PE+l3SejPqGQ961Wo+hbxd/y9JyZy/jZ9WW8XA6eEXXtecRY33NspwT58mBXDgZLIM/C3W0qfrGCiOPxwk0RpMo7YMbmYVPLG75AzihDtQ2F7P5edHz7v0yCAejrN23hi1LHb4Uku8tC2jzrH5eUpKfZuqap8DRcbdXq5je3oeuLSUu39FrzsDEmennS0eaD4jTsB5Sy2wld/UCmzV0QenUtPdBaFLU2Rxos3xJW4a2KyENRm5TGVNR/NAWpoacLed3zqDmb3K13WwskTGE1/mXRl+o0T0BVOBXuHXQ+nqATnGuAbw97LhhOeBZ/jA2yWBsaTxdjhB3E3uKWYZYdGIIgOwrZdM0RrVSgepg7NP+vh9iO70ckEzP+w4yws2ElKE3ZiOexEmrkFmqlxQW08b1FMaKJwpfsNiHkwW6u1jq1oeEBIzUrAMmo92uAjDAHKfn7FOsx5RVg20EKP9Rk9l2YKRA5dGJFI= @@ -19,84 +18,80 @@ cache: - $HOME/.gradle/wrapper/ - $HOME/.android/build-cache/ -stages: -- name: Build debug - if: branch IN (develop, master) -- name: Build and deploy release on tags - if: tag IS present - -android_phases: -- phase: &before_install +before_install: # download and unzip Android SDK command line tools - - wget -nv https://dl.google.com/android/repository/$ANDROID_SDK_TOOLS - - unzip -q $ANDROID_SDK_TOOLS -d $HOME/sdk - # add deprecated java ee module to jdk10 to run the sdkmanager - - export JAVA_OPTS="-XX:+IgnoreUnrecognizedVMOptions --add-modules java.se.ee" + - wget -nv https://dl.google.com/android/repository/$ANDROID_SDK_CMD_TOOLS + - mkdir -p $HOME/sdk/cmdline-tools && unzip -q $ANDROID_SDK_CMD_TOOLS -d $HOME/sdk/cmdline-tools # set SDK tools path variable and ANDROID_HOME - - export PATH=$PATH:$HOME/sdk/tools/bin + - export PATH=$PATH:$HOME/sdk/cmdline-tools/tools/bin - export ANDROID_HOME=$HOME/sdk # create empty cfg file to prevent sdkmanager warning message - mkdir -p $HOME/.android && touch $HOME/.android/repositories.cfg # decrypt private keystore - openssl aes-256-cbc -K $encrypted_a8fbd6bbc21d_key -iv $encrypted_a8fbd6bbc21d_iv -in keystore.jks.enc -out keystore.jks -d -- phase: &install +install: # accept licenses for all available packages that have not already been accepted - yes | sdkmanager --licenses >/dev/null -- phase: &before_script +before_script: # set executable flag for gradle wrapper - chmod +x gradlew # create dir for gradle settings - mkdir -p $HOME/.gradle - # disable gradle daemon for current user + # disable gradle daemon - echo "org.gradle.daemon=false" >> $HOME/.gradle/gradle.properties # set gradle log format to plain - echo "org.gradle.console=plain" >> $HOME/.gradle/gradle.properties - # enable gradle build cache - - echo "org.gradle.caching=true" >> $HOME/.gradle/gradle.properties # log all gradle warnings - echo "org.gradle.warning.mode=all" >> $HOME/.gradle/gradle.properties -- phase: &before_cache - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - - rm -f $HOME/.gradle/caches/*/fileHashes/fileHashes.bin - - rm -f $HOME/.gradle/caches/*/fileHashes/fileHashes.lock - - rm -f $HOME/.gradle/caches/*/javaCompile/javaCompile.lock - - rm -f $HOME/.gradle/caches/*/executionHistory/executionHistory.lock - - rm -f $HOME/.gradle/caches/journal-1/file-access.bin - - rm -f $HOME/.gradle/caches/journal-1/journal-1.lock - - rm -f $HOME/.gradle/caches/transforms-1/transforms-1.lock - - rm -f $HOME/.gradle/caches/user-id.txt.lock + # control gradle build cache + - if [[ $CACHING == "true" ]]; then + echo "org.gradle.caching=true" >> $HOME/.gradle/gradle.properties; + echo "android.enableBuildCache=true" >> $HOME/.gradle/gradle.properties; + else + echo "org.gradle.caching=false" >> $HOME/.gradle/gradle.properties; + echo "android.enableBuildCache=false" >> $HOME/.gradle/gradle.properties; + fi +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -rf $HOME/.gradle/caches/*/plugin-resolution/ + - rm -f $HOME/.gradle/caches/*/fileHashes/fileHashes.bin + - rm -f $HOME/.gradle/caches/*/fileHashes/fileHashes.lock + - rm -f $HOME/.gradle/caches/*/javaCompile/javaCompile.lock + - rm -f $HOME/.gradle/caches/*/executionHistory/executionHistory.bin + - rm -f $HOME/.gradle/caches/*/executionHistory/executionHistory.lock + - rm -f $HOME/.gradle/caches/journal-1/file-access.bin + - rm -f $HOME/.gradle/caches/journal-1/journal-1.lock + - rm -f $HOME/.gradle/caches/transforms-1/transforms-1.lock + - rm -f $HOME/.gradle/caches/user-id.txt.lock # only cache latest gradle version used by the wrapper # list content in wrapper/dist sorted by modification time and remove entries starting by the second entry - ls -d $HOME/.gradle/wrapper/dists/* -1t | tail -n +2 | xargs rm -rf -- phase: &deploy - provider: releases - api_key: - secure: J5U/QxYAcGZn/pZeU8+YK7ani5dn64McDEVj5BxTBbywfhfpabzug+6H8k6Mnvs8m8CEvi2NsgptyvsxMt6952dNFI9F0URRsDkDdBvKQKdBmhCmCWVckRushfrn5k8RTjpJFYExiuDw6mSPZVtVzdXgHTgiZzPW20skROgoOq6Jb2CMa9awyI1Pbni7Emirkdl3N+h8krCyi/T06va1QebbWYsdLem8EvPgREBV+fDZ8RR+ABnT68aV7Jyq5YWortThT0TRAQ5f17C/T/aDWU7TqL4+HbfGEMvlO4vFYKUHNhxB6ZssaOpLSRVZKq4kecr2PQWIzQX+VFo4Fyxe3kTqfgidR2ptihkAERFz5FCHsEDDSRliatUUFpXxNLaa4ZIooo5p5uThRniu9COjdwuJZtFUIqstL1IrLntv4+3P2SY2BfdsTkgSuq7NT37u6MbT/cdr+dO7jgCoRIyxRzQGoeViALjzwIdXS2iBCwEpex9IDdvjsHmFJIo+8IsJGeGUCRsJL0tFBtZ8lIks1bh+t7pxKywhS3vXGny2ZCaIdA55g0b5N0D64P12ibWI80B7EAEv43Xmb5oVkVYdq9PdPJFoG/b4nDy8NfbQCWv9P/xgP6KwogAPsCJJ7tS4qn2HV6eUKrsj7ioBwpHsn/3ZEL0gU3sBnGJk20hyZyc= - file_glob: true - file: - - $TRAVIS_BUILD_DIR/app/build/outputs/apk/release/*.apk - - $TRAVIS_BUILD_DIR/app/build/outputs/mapping/release/mapping.txt - skip_cleanup: true - draft: false - on: - tags: true - name: $TRAVIS_TAG - tag_name: $TRAVIS_TAG - body: "Generated release from Travis CI for build $TRAVIS_BUILD_NUMBER" jobs: include: - - stage: Build debug - before_install: *before_install - install: *install - before_script: *before_script + - # Build debug + if: branch IN (develop, master) + env: CACHING=true script: "./gradlew assembleDebug --scan" - before_cache: *before_cache - - stage: Build and deploy release on tags - before_install: *before_install - install: *install - before_script: *before_script - script: "./gradlew assembleRelease -Pmy_storepass=$storepass -Pmy_keyalias=$keyalias -Pmy_keypass=$keypass --scan" - before_cache: *before_cache - deploy: *deploy \ No newline at end of file + - # Build and deploy release on tags + if: tag IS present + env: CACHING=false + script: + - "./gradlew assembleRelease -Pmy_storepass=$storepass -Pmy_keyalias=$keyalias -Pmy_keypass=$keypass --scan" + - "./gradlew :app:bundleRelease -Pmy_storepass=$storepass -Pmy_keyalias=$keyalias -Pmy_keypass=$keypass" + deploy: + provider: releases + edge: true + token: + secure: J5U/QxYAcGZn/pZeU8+YK7ani5dn64McDEVj5BxTBbywfhfpabzug+6H8k6Mnvs8m8CEvi2NsgptyvsxMt6952dNFI9F0URRsDkDdBvKQKdBmhCmCWVckRushfrn5k8RTjpJFYExiuDw6mSPZVtVzdXgHTgiZzPW20skROgoOq6Jb2CMa9awyI1Pbni7Emirkdl3N+h8krCyi/T06va1QebbWYsdLem8EvPgREBV+fDZ8RR+ABnT68aV7Jyq5YWortThT0TRAQ5f17C/T/aDWU7TqL4+HbfGEMvlO4vFYKUHNhxB6ZssaOpLSRVZKq4kecr2PQWIzQX+VFo4Fyxe3kTqfgidR2ptihkAERFz5FCHsEDDSRliatUUFpXxNLaa4ZIooo5p5uThRniu9COjdwuJZtFUIqstL1IrLntv4+3P2SY2BfdsTkgSuq7NT37u6MbT/cdr+dO7jgCoRIyxRzQGoeViALjzwIdXS2iBCwEpex9IDdvjsHmFJIo+8IsJGeGUCRsJL0tFBtZ8lIks1bh+t7pxKywhS3vXGny2ZCaIdA55g0b5N0D64P12ibWI80B7EAEv43Xmb5oVkVYdq9PdPJFoG/b4nDy8NfbQCWv9P/xgP6KwogAPsCJJ7tS4qn2HV6eUKrsj7ioBwpHsn/3ZEL0gU3sBnGJk20hyZyc= + file: + - $TRAVIS_BUILD_DIR/app/build/outputs/bundle/release/*.aab + - $TRAVIS_BUILD_DIR/app/build/outputs/apk/release/*.apk + - $TRAVIS_BUILD_DIR/app/build/outputs/mapping/release/mapping.txt + draft: false + on: + tags: true + name: $TRAVIS_TAG + tag_name: $TRAVIS_TAG + release_notes: "Generated release from Travis CI for build $TRAVIS_BUILD_NUMBER" + prerelease: true \ No newline at end of file diff --git a/README.md b/README.md index 8b622408..90e983f4 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ [](https://raw.githubusercontent.com/G00fY2/DeveloperWidget/gh-pages/media/store_screenshot_4.png) [](https://raw.githubusercontent.com/G00fY2/DeveloperWidget/gh-pages/media/store_screenshot_5.png) +created with [App Mockup](https://app-mockup.com) + ## Description The app was built from a developer for developers. You may know the hassle of having multiple physical devices running different software. This app will help you keep track of important device information and allows you to organize your apps and local APK files. You will never again struggle to find APK files using a file browser or search for the app settings menu on a custom manufacturer UI. diff --git a/app/build.gradle b/app/build.gradle index 1bab2780..cab961bd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,8 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' apply plugin: 'eu.appcom.gradle.android-versioning' -androidExtensions { - experimental = true -} - android { compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion @@ -41,6 +36,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + buildFeatures { + viewBinding = true + } versioning { baseName = 'developerwidget' } @@ -61,14 +59,18 @@ android { kotlinOptions { jvmTarget = '1.8' } - kapt { - javacOptions { - option('-source', '8') - option('-target', '8') +} + +repositories { + google() + mavenCentral() + jcenter { + content { + includeModule 'com.g00fy2', 'versioncompare' + includeModule 'org.jetbrains.trove4j', 'trove4j' // required by com.android.tools.lint:lint-gradle } } } - dependencies { // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" @@ -78,6 +80,7 @@ dependencies { implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation "androidx.core:core-ktx:$coreKtxVersion" implementation "androidx.activity:activity:$activityVersion" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "androidx.recyclerview:recyclerview:$recyclerviewVersion" implementation "androidx.constraintlayout:constraintlayout:$constraintlayoutVersion" implementation "androidx.vectordrawable:vectordrawable:$vectorDrawableVersion" @@ -95,4 +98,5 @@ dependencies { implementation "com.google.dagger:dagger-android:$daggerVersion" implementation "com.google.dagger:dagger-android-support:$daggerVersion" kapt "com.google.dagger:dagger-android-processor:$daggerVersion" + compileOnly 'javax.annotation:javax.annotation-api:1.3.2' } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c00b0a2e..ce2b2616 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,5 +1 @@ -# Ensure the custom, fast service loader implementation is removed. --assumevalues class kotlinx.coroutines.internal.MainDispatcherLoader { - boolean FAST_SERVICE_LOADER_ENABLED return false; -} --checkdiscard class kotlinx.coroutines.internal.FastServiceLoader \ No newline at end of file +# Add project specific ProGuard rules here. \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 39129172..1ef531ce 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ + package="com.g00fy2.developerwidget" + tools:ignore="LockedOrientationActivity"> diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutActivity.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutActivity.kt index 2dbef055..45156673 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutActivity.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutActivity.kt @@ -5,124 +5,141 @@ import android.content.ComponentName import android.content.pm.PackageManager import android.os.Build.VERSION import android.os.Build.VERSION_CODES -import android.os.Bundle import android.view.MenuItem import androidx.appcompat.app.AppCompatDelegate +import androidx.core.view.updatePadding +import androidx.viewbinding.ViewBinding import com.g00fy2.developerwidget.BuildConfig import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.activities.widgetconfig.ConfigLauncherActivity import com.g00fy2.developerwidget.base.BaseActivity import com.g00fy2.developerwidget.base.BaseContract.BasePresenter -import kotlinx.android.synthetic.main.activity_about.* +import com.g00fy2.developerwidget.databinding.ActivityAboutBinding +import com.g00fy2.developerwidget.ktx.doOnApplyWindowInsets +import com.g00fy2.developerwidget.ktx.gesturalNavigationMode import javax.inject.Inject -class AboutActivity : BaseActivity(R.layout.activity_about), AboutContract.AboutView { +class AboutActivity : BaseActivity(), AboutContract.AboutView { @Inject lateinit var presenter: AboutContract.AboutPresenter + private lateinit var binding: ActivityAboutBinding override fun providePresenter(): BasePresenter = presenter - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - initView() + override fun setViewBinding(): ViewBinding { + binding = ActivityAboutBinding.inflate(layoutInflater) + return binding } - override fun onResume() { - super.onResume() - updateThemeToggleView() - updateLauncherIconSwitch() - updateLauncherIconItem() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == id.home) { - finish() - true - } else { - super.onOptionsItemSelected(item) - } - } - - private fun initView() { + override fun initView() { supportActionBar?.setDisplayHomeAsUpEnabled(true) - setActionbarElevationListener(about_root_scrollview) + setActionbarElevationListener(binding.aboutRootScrollview) - app_version_textview.text = String.format(getString(R.string.app_version), BuildConfig.VERSION_NAME) + binding.appVersionTextview.text = String.format(getString(R.string.app_version), BuildConfig.VERSION_NAME) - theme_item.init { + binding.themeItem.init { title(R.string.app_theme) action { presenter.toggleDayNightMode() } } - privacy_item.init { + binding.privacyItem.init { icon(R.drawable.ic_privacy_logo) title(R.string.privacy_policy) action { presenter.openUrl(PRIVACY_POLICY) } } - license_item.init { + binding.sourceCodeItem.init { + icon(R.drawable.ic_github_logo_shape) + title(R.string.source_code) + action { presenter.openUrl(GITHUB_PROJECT) } + } + binding.changelogItem.init { + icon(R.drawable.ic_changes_logo) + title(R.string.changelog) + action { presenter.openUrl(CHANGES) } + } + binding.licenseItem.init { icon(R.drawable.ic_open_source_logo) title(R.string.license) description(R.string.mit_license) action { presenter.openUrl(MIT_LICENSE) } } - feedback_item.init { + binding.feedbackItem.init { icon(R.drawable.ic_feedback) title(R.string.feedback) description(R.string.feedback_description) action { showFeedbackOptions() } } - source_code_item.init { - icon(R.drawable.ic_github_logo_shape) - title(R.string.source_code) - action { presenter.openUrl(GITHUB_PROJECT) } - } - changelog_item.init { - icon(R.drawable.ic_changes_logo) - title(R.string.changelog) - action { presenter.openUrl(CHANGES) } - } - author_header.init { + binding.authorHeader.init { title(R.string.author) } - twitter_item.init { + binding.twitterItem.init { icon(R.drawable.ic_twitter_logo) title(R.string.twitter) description(R.string.twitter_username) action { presenter.openUrl(TWITTER_USER) } } - github_item.init { + binding.githubItem.init { icon(R.drawable.ic_github_logo_shape) title(R.string.github) description(R.string.github_username) action { presenter.openUrl(GITHUB_USER) } } - licenses_header.init { + binding.licensesHeader.init { title(R.string.licenses) } - open_source_licenses_item.init { + binding.openSourceLicensesItem.init { title(R.string.open_source_licenses) description(R.string.open_source_licenses_description) action { presenter.openUrl(OSS_LICENSES) } } - image_licenses_item.init { + binding.imageLicensesItem.init { title(R.string.icon_credits) description(R.string.icon_credits_description) action { presenter.openUrl(ICON_CREDITS) } } - hide_launcher_icon_item.init { + binding.hideLauncherIconItem.init { title(R.string.show_app_icon) description(R.string.show_app_icon_description) action { toggleLauncherIcon() } } - build_number_item.init { + binding.buildNumberItem.init { title(R.string.build_number) description(BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ") " + BuildConfig.BUILD_TYPE) action { presenter.honorClicking() } } + if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) { + binding.aboutRootScrollview.apply { + doOnApplyWindowInsets { _, insets, padding, _ -> + updatePadding(bottom = padding.bottom + insets.systemWindowInsetBottom) + } + viewTreeObserver.addOnScrollChangedListener { + val scrollableRange = getChildAt(0).bottom - height + paddingBottom + if (!gesturalNavigationMode()) { + clipToPadding = (scrollY >= scrollableRange) + } + } + } + } + } + + override fun onResume() { + super.onResume() + updateThemeToggleView() + updateLauncherIconSwitch() + updateLauncherIconItem() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == id.home) { + finish() + true + } else { + super.onOptionsItemSelected(item) + } } override fun updateThemeToggleView() { - theme_item.let { + binding.themeItem.let { when (dayNightController.getCurrentDefaultMode()) { AppCompatDelegate.MODE_NIGHT_YES -> it.icon(R.drawable.ic_mode_night).description(R.string.night_mode) AppCompatDelegate.MODE_NIGHT_NO -> it.icon(R.drawable.ic_mode_day).description(R.string.day_mode) @@ -131,10 +148,10 @@ class AboutActivity : BaseActivity(R.layout.activity_about), AboutContract.About } } - private fun updateLauncherIconSwitch() = hide_launcher_icon_item.switch(!isLauncherIconDisabled()) + private fun updateLauncherIconSwitch() = binding.hideLauncherIconItem.switch(!isLauncherIconDisabled()) private fun updateLauncherIconItem() { - if (VERSION.SDK_INT >= VERSION_CODES.Q) hide_launcher_icon_item.isEnabled = isLauncherIconDisabled() + if (VERSION.SDK_INT >= VERSION_CODES.Q) binding.hideLauncherIconItem.isEnabled = isLauncherIconDisabled() } private fun showFeedbackOptions() { diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutFeedbackDialog.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutFeedbackDialog.kt index 19344bb8..5fc55e7b 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutFeedbackDialog.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/AboutFeedbackDialog.kt @@ -1,20 +1,22 @@ package com.g00fy2.developerwidget.activities.about import android.content.Context -import android.widget.TextView import androidx.appcompat.app.AppCompatDialog import com.g00fy2.developerwidget.R +import com.g00fy2.developerwidget.databinding.AboutFeedbackDialogBinding class AboutFeedbackDialog(context: Context) { + private var binding: AboutFeedbackDialogBinding private val dialog = AppCompatDialog(context, R.style.DialogTheme).apply { setCancelable(true) setCanceledOnTouchOutside(true) - setContentView(R.layout.about_feedback_dialog) + binding = AboutFeedbackDialogBinding.inflate(layoutInflater) + setContentView(binding.root) } fun mailAction(mailIssueAction: () -> Unit): AboutFeedbackDialog { - dialog.findViewById(R.id.mailTextView)?.setOnClickListener { + binding.mailTextView.setOnClickListener { dialog.dismiss() mailIssueAction() } @@ -22,7 +24,7 @@ class AboutFeedbackDialog(context: Context) { } fun githubAction(githubIssueAction: () -> Unit): AboutFeedbackDialog { - dialog.findViewById(R.id.githubTextView)?.setOnClickListener { + binding.githubTextView.setOnClickListener { dialog.dismiss() githubIssueAction() } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutHeaderLayout.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutHeaderLayout.kt index d32ebe15..a9a3cb79 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutHeaderLayout.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutHeaderLayout.kt @@ -5,19 +5,18 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import androidx.annotation.StringRes -import com.g00fy2.developerwidget.R -import kotlinx.android.synthetic.main.about_item_header.view.* +import com.g00fy2.developerwidget.databinding.AboutItemHeaderBinding -class AboutHeaderLayout : FrameLayout { +class AboutHeaderLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { - constructor(context: Context) : this(context, null) - constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { - LayoutInflater.from(context).inflate(R.layout.about_item_header, this, true) - } + private val binding = AboutItemHeaderBinding.inflate(LayoutInflater.from(context), this) fun title(@StringRes titleRes: Int): AboutHeaderLayout { - header_textview.setText(titleRes) + binding.headerTextview.setText(titleRes) return this } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutItemLayout.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutItemLayout.kt index 6b1396ce..06f4378c 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutItemLayout.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/about/views/AboutItemLayout.kt @@ -3,66 +3,70 @@ package com.g00fy2.developerwidget.activities.about.views import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater -import android.view.View import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.children import androidx.core.view.isVisible -import com.g00fy2.developerwidget.R -import kotlinx.android.synthetic.main.about_item.view.* +import com.g00fy2.developerwidget.databinding.AboutItemBinding +import com.g00fy2.developerwidget.ktx.addRipple +import com.google.android.material.textview.MaterialTextView -class AboutItemLayout : ConstraintLayout { +class AboutItemLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + ConstraintLayout(context, attrs, defStyleAttr) { - constructor(context: Context) : this(context, null) - constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { - LayoutInflater.from(context).inflate(R.layout.about_item, this, true) - } + private val binding = AboutItemBinding.inflate(LayoutInflater.from(context), this) override fun setEnabled(enabled: Boolean) { super.setEnabled(enabled) - for (child in constraint_layout.children) child.isEnabled = enabled + for (child in children) { + if (child::class == MaterialTextView::class) { + child.alpha = if (enabled) 1f else 0.38f + } else { + child.isEnabled = enabled + } + } } fun icon(@DrawableRes iconRes: Int): AboutItemLayout { - icon_imageview.setImageResource(iconRes) - icon_imageview.visibility = View.VISIBLE + binding.iconImageview.setImageResource(iconRes) + binding.iconImageview.isVisible = true return this } fun title(@StringRes titleRes: Int): AboutItemLayout { - title_textview.setText(titleRes) - title_textview.visibility = View.VISIBLE + binding.titleTextview.setText(titleRes) + binding.titleTextview.isVisible = true return this } fun description(@StringRes descriptionRes: Int): AboutItemLayout { - description_textview.setText(descriptionRes) - description_textview.visibility = View.VISIBLE + binding.descriptionTextview.setText(descriptionRes) + binding.descriptionTextview.isVisible = true return this } fun description(description: String): AboutItemLayout { if (description.isNotBlank()) { - description_textview.text = description - description_textview.visibility = View.VISIBLE + binding.descriptionTextview.text = description + binding.descriptionTextview.isVisible = true } else { - description_textview.visibility = View.GONE + binding.descriptionTextview.isVisible = false } return this } fun switch(on: Boolean): AboutItemLayout { - setting_switch.isChecked = on - setting_switch.visibility = View.VISIBLE + binding.settingSwitch.isChecked = on + binding.settingSwitch.isVisible = true return this } fun action(action: () -> Unit): AboutItemLayout { + addRipple() setOnClickListener { action() - if (setting_switch.isVisible) setting_switch.isChecked = !setting_switch.isChecked + if (binding.settingSwitch.isVisible) binding.settingSwitch.isChecked = !binding.settingSwitch.isChecked } return this } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkActivity.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkActivity.kt index f8e9a5ca..446eba38 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkActivity.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkActivity.kt @@ -1,47 +1,51 @@ package com.g00fy2.developerwidget.activities.apkinstall -import android.os.Bundle import android.view.View import androidx.appcompat.widget.TooltipCompat import androidx.core.view.ViewCompat import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewbinding.ViewBinding import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.base.BaseActivity import com.g00fy2.developerwidget.base.BaseContract.BasePresenter -import kotlinx.android.synthetic.main.activity_apk.* +import com.g00fy2.developerwidget.databinding.ActivityApkBinding import javax.inject.Inject -class ApkActivity : BaseActivity(R.layout.activity_apk, true), ApkContract.ApkView { +class ApkActivity : BaseActivity(true), ApkContract.ApkView { @Inject lateinit var presenter: ApkContract.ApkPresenter private lateinit var adapter: ApkAdapter + private lateinit var binding: ActivityApkBinding override fun providePresenter(): BasePresenter = presenter - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + override fun setViewBinding(): ViewBinding { + binding = ActivityApkBinding.inflate(layoutInflater) + return binding + } + override fun initView() { adapter = ApkAdapter() adapter.setOnApkClicked { apkFile -> presenter.installApk(apkFile) } adapter.setOnApkSelect { selectedCount -> showOptions(selectedCount > 0) } adapter.setCommitCallback(Runnable { adapter.itemCount.let { - recyclerview.overScrollMode = if (it == 0) View.OVER_SCROLL_NEVER else View.OVER_SCROLL_ALWAYS - no_items_textview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE - no_items_imageview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE + binding.recyclerview.overScrollMode = if (it == 0) View.OVER_SCROLL_NEVER else View.OVER_SCROLL_ALWAYS + binding.noItemsTextview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE + binding.noItemsImageview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE } }) - recyclerview.setHasFixedSize(true) - recyclerview.layoutManager = LinearLayoutManager(this) - recyclerview.adapter = adapter + binding.recyclerview.setHasFixedSize(true) + binding.recyclerview.layoutManager = LinearLayoutManager(this) + binding.recyclerview.adapter = adapter - cancel_textview.setOnClickListener { finish() } - TooltipCompat.setTooltipText(delete_imageview, delete_imageview.contentDescription) - delete_imageview.setOnClickListener { showConfirmationDialog() } - TooltipCompat.setTooltipText(clear_imageview, clear_imageview.contentDescription) - clear_imageview.setOnClickListener { + binding.cancelTextview.setOnClickListener { finish() } + TooltipCompat.setTooltipText(binding.clearImageview, binding.clearImageview.contentDescription) + binding.deleteImageview.setOnClickListener { showConfirmationDialog() } + TooltipCompat.setTooltipText(binding.clearImageview, binding.clearImageview.contentDescription) + binding.clearImageview.setOnClickListener { adapter.clearSelectedList() showOptions(false) } @@ -49,34 +53,32 @@ class ApkActivity : BaseActivity(R.layout.activity_apk, true), ApkContract.ApkVi override fun onResume() { super.onResume() - initView() + if (adapter.itemCount == 0) { + binding.progressbar.visibility = View.VISIBLE + binding.noItemsImageview.visibility = View.INVISIBLE + binding.noItemsTextview.text = getString(R.string.scanning_apks) + binding.noItemsTextview.visibility = View.VISIBLE + } } override fun toggleResultView(apkFiles: List, missingPermissions: Boolean) { if (missingPermissions) { - progressbar.visibility = View.GONE - no_items_textview.text = getString(R.string.missing_permissions) + binding.progressbar.visibility = View.GONE + binding.noItemsTextview.text = getString(R.string.missing_permissions) } else { - no_items_textview.visibility = View.INVISIBLE - no_items_textview.text = getString(R.string.no_apk_found) - ViewCompat.animate(progressbar).alpha(0f) + binding.noItemsTextview.visibility = View.INVISIBLE + binding.noItemsTextview.text = getString(R.string.no_apk_found) + ViewCompat.animate(binding.progressbar).alpha(0f) .setDuration(resources.getInteger(android.R.integer.config_shortAnimTime).toLong()).withEndAction { - progressbar.visibility = View.INVISIBLE - progressbar.alpha = 1f + binding.progressbar.visibility = View.INVISIBLE + binding.progressbar.alpha = 1f }.start() } adapter.submitList(apkFiles) } - private fun initView() { - progressbar.visibility = View.VISIBLE - no_items_imageview.visibility = View.INVISIBLE - no_items_textview.text = getString(R.string.scanning_apks) - no_items_textview.visibility = View.VISIBLE - } - private fun showOptions(show: Boolean) { - delete_header_group.visibility = if (show) View.VISIBLE else View.GONE + binding.deleteHeaderGroup.visibility = if (show) View.VISIBLE else View.GONE } private fun showConfirmationDialog() { @@ -84,7 +86,7 @@ class ApkActivity : BaseActivity(R.layout.activity_apk, true), ApkContract.ApkVi deleteMessage(adapter.getSelectedCount()) deleteAction { showOptions(false) - progressbar.visibility = View.VISIBLE + binding.progressbar.visibility = View.VISIBLE presenter.deleteApkFiles(adapter.getSelectedApkFilesAndClear()) } }.show() diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkAdapter.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkAdapter.kt index 5f219120..8059ba29 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkAdapter.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkAdapter.kt @@ -11,8 +11,7 @@ import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.activities.apkinstall.ApkAdapter.ApkViewHolder import com.g00fy2.developerwidget.base.BaseAdapter import com.g00fy2.developerwidget.base.BaseViewHolder -import kotlinx.android.synthetic.main.apk_item.* -import kotlinx.android.synthetic.main.apk_item.view.* +import com.g00fy2.developerwidget.databinding.ApkItemBinding class ApkAdapter : BaseAdapter(ApksDiffUtilsCallback()) { @@ -20,19 +19,34 @@ class ApkAdapter : BaseAdapter(ApksDiffUtilsCallback()) private var onApkClicked: ((ApkFile?) -> Unit) = {} private var onApkSelected: ((Int) -> Unit) = {} - inner class ApkViewHolder(containerView: View) : BaseViewHolder(containerView) { + inner class ApkViewHolder(val binding: ApkItemBinding) : BaseViewHolder(binding) { + override fun onBind(item: ApkFile) { + item.run { + binding.filenameTextview.text = fileName + binding.appNameTextview.text = appName + binding.appVersionTextview.text = + String.format(itemView.context.getString(R.string.apk_version), item.versionName, item.versionCode) + binding.fileSizeTextview.text = item.size + binding.appDebugImageview.visibility = if (item.debuggable) View.VISIBLE else View.INVISIBLE + binding.fileDateTextview.text = item.lastModified + binding.appIconImageview.setImageDrawable(item.appIcon) + if (VERSION.SDK_INT >= VERSION_CODES.O) { + binding.appIconImageview.setBackgroundResource(if (item.appIcon is InsetDrawable) R.drawable.bg_adaptive_launcher_icon else 0) + } + } + setSelected(adapterPosition) + } + fun setSelected(position: Int) { selectedPositions.contains(position).let { selected -> - itemView.app_icon_imageview.visibility = - if (selected) View.INVISIBLE else View.VISIBLE - itemView.selected_icon_imageview.visibility = - if (selected) View.VISIBLE else View.INVISIBLE + binding.appIconImageview.visibility = if (selected) View.INVISIBLE else View.VISIBLE + binding.selectedIconImageview.visibility = if (selected) View.VISIBLE else View.INVISIBLE } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ApkViewHolder { - return ApkViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.apk_item, parent, false)).apply { + return ApkViewHolder(ApkItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)).apply { addRipple() itemView.setOnClickListener { adapterPosition.let { @@ -64,29 +78,11 @@ class ApkAdapter : BaseAdapter(ApksDiffUtilsCallback()) } } - override fun onBindViewHolder(holderApk: ApkViewHolder, position: Int) { - holderApk.apply { - val apkFile = getItem(position) - filename_textview.text = apkFile.fileName - app_name_textview.text = apkFile.appName - app_version_textview.text = - String.format(itemView.context.getString(R.string.apk_version), apkFile.versionName, apkFile.versionCode) - file_size_textview.text = apkFile.size - app_debug_imageview.visibility = if (apkFile.debuggable) View.VISIBLE else View.INVISIBLE - file_date_textview.text = apkFile.lastModified - app_icon_imageview.setImageDrawable(apkFile.appIcon) - if (VERSION.SDK_INT >= VERSION_CODES.O) { - app_icon_imageview.setBackgroundResource(if (apkFile.appIcon is InsetDrawable) R.drawable.bg_adaptive_launcher_icon else 0) - } - setSelected(position) - } - } - - override fun onBindViewHolder(holderApk: ApkViewHolder, position: Int, payloads: List) { + override fun onBindViewHolder(holder: ApkViewHolder, position: Int, payloads: List) { if (payloads.isEmpty()) { - onBindViewHolder(holderApk, position) + super.onBindViewHolder(holder, position) } else { - holderApk.setSelected(position) + holder.setSelected(position) } } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkDeleteDialog.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkDeleteDialog.kt index 74a011ba..641ec618 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkDeleteDialog.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkDeleteDialog.kt @@ -1,20 +1,22 @@ package com.g00fy2.developerwidget.activities.apkinstall import android.content.Context -import android.widget.TextView import androidx.appcompat.app.AppCompatDialog import com.g00fy2.developerwidget.R +import com.g00fy2.developerwidget.databinding.ApkDeleteDialogBinding class ApkDeleteDialog(context: Context) { + private var binding: ApkDeleteDialogBinding private val dialog = AppCompatDialog(context, R.style.DialogTheme).apply { setCancelable(true) - setContentView(R.layout.apk_delete_dialog) - findViewById(R.id.dialog_cancel_textview)?.setOnClickListener { dismiss() } + binding = ApkDeleteDialogBinding.inflate(layoutInflater) + setContentView(binding.root) + binding.dialogCancelTextview.setOnClickListener { dismiss() } } fun deleteMessage(count: Int): ApkDeleteDialog { - dialog.findViewById(R.id.dialog_message_textview)?.text = if (count > 1) String.format( + binding.dialogMessageTextview.text = if (count > 1) String.format( dialog.context.resources.getString(R.string.message_delete_multi), count ) else dialog.context.resources.getString(R.string.message_delete_single) @@ -23,7 +25,7 @@ class ApkDeleteDialog(context: Context) { } fun deleteAction(deleteAction: () -> Unit): ApkDeleteDialog { - dialog.findViewById(R.id.dialog_delete_textview)?.setOnClickListener { + binding.dialogDeleteTextview.setOnClickListener { dialog.dismiss() deleteAction() } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkPresenterImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkPresenterImpl.kt index bd659ab2..e06a7bc6 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkPresenterImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/apkinstall/ApkPresenterImpl.kt @@ -3,6 +3,7 @@ package com.g00fy2.developerwidget.activities.apkinstall import android.Manifest import androidx.lifecycle.Lifecycle.Event import androidx.lifecycle.OnLifecycleEvent +import androidx.lifecycle.lifecycleScope import com.g00fy2.developerwidget.base.BasePresenterImpl import com.g00fy2.developerwidget.controllers.IntentController import com.g00fy2.developerwidget.controllers.PermissionController @@ -34,7 +35,7 @@ class ApkPresenterImpl @Inject constructor() : BasePresenterImpl(), ApkContract. @OnLifecycleEvent(Event.ON_RESUME) fun scanStorageForApks() { if (permissionController.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - launch { + view.lifecycleScope.launch { withContext(Dispatchers.IO) { mutableSetOf().apply { for (dir in storageDirsController.getStorageDirectories()) { @@ -46,7 +47,7 @@ class ApkPresenterImpl @Inject constructor() : BasePresenterImpl(), ApkContract. } } } else { - view.toggleResultView(emptyList(), missingPermissions = true) + view.toggleResultView(emptyList(), true) } } @@ -66,7 +67,7 @@ class ApkPresenterImpl @Inject constructor() : BasePresenterImpl(), ApkContract. override fun deleteApkFiles(apkFiles: List?) { if (permissionController.hasPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) ) { - launch { + view.lifecycleScope.launch { withContext(Dispatchers.IO) { apkFiles?.let { files -> for (apkFile in files) { diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivity.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivity.kt index 8b45a17b..4bceadf7 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivity.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivity.kt @@ -6,38 +6,37 @@ import android.graphics.PorterDuff import android.graphics.drawable.Drawable import android.os.Build.VERSION import android.os.Build.VERSION_CODES -import android.os.Bundle -import android.text.Editable import android.text.InputFilter -import android.text.TextWatcher import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.inputmethod.EditorInfo -import android.widget.EditText import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.TooltipCompat import androidx.core.content.res.ResourcesCompat import androidx.core.view.ViewCompat import androidx.core.view.doOnPreDraw import androidx.core.view.isVisible +import androidx.core.widget.doAfterTextChanged import androidx.interpolator.view.animation.FastOutLinearInInterpolator import androidx.interpolator.view.animation.LinearOutSlowInInterpolator import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewbinding.ViewBinding import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.base.BaseActivity import com.g00fy2.developerwidget.base.BaseContract.BasePresenter +import com.g00fy2.developerwidget.databinding.ActivityAppsBinding import com.g00fy2.developerwidget.ktx.collapse import com.g00fy2.developerwidget.ktx.expand import com.g00fy2.developerwidget.ktx.hideKeyboard import com.google.android.material.chip.Chip -import kotlinx.android.synthetic.main.activity_apps.* import javax.inject.Inject -class AppsActivity : BaseActivity(R.layout.activity_apps, true), AppsContract.AppsView { +class AppsActivity : BaseActivity(true), AppsContract.AppsView { @Inject lateinit var presenter: AppsContract.AppsPresenter + private lateinit var binding: ActivityAppsBinding private lateinit var adapter: AppsAdapter private var scrollToTopAfterCommit = false @@ -45,44 +44,47 @@ class AppsActivity : BaseActivity(R.layout.activity_apps, true), AppsContract.Ap override fun providePresenter(): BasePresenter = presenter - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + override fun setViewBinding(): ViewBinding { + binding = ActivityAppsBinding.inflate(layoutInflater) + return binding + } + override fun initView() { adapter = AppsAdapter() adapter.setOnAppClicked { appInfo -> presenter.openAppSettingsActivity(appInfo) } adapter.setCommitCallback(Runnable { adapter.itemCount.let { - no_items_textview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE - no_items_imageview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE - recyclerview.overScrollMode = if (it == 0) View.OVER_SCROLL_NEVER else View.OVER_SCROLL_ALWAYS + binding.noItemsTextview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE + binding.noItemsImageview.visibility = if (it == 0) View.VISIBLE else View.INVISIBLE + binding.recyclerview.overScrollMode = if (it == 0) View.OVER_SCROLL_NEVER else View.OVER_SCROLL_ALWAYS } if (scrollToTopAfterCommit) { scrollToTopAfterCommit = false - recyclerview.scrollToPosition(0) + binding.recyclerview.scrollToPosition(0) } }) - recyclerview.setHasFixedSize(true) - recyclerview.itemAnimator = null - recyclerview.layoutManager = LinearLayoutManager(this) - recyclerview.adapter = adapter + binding.recyclerview.setHasFixedSize(true) + binding.recyclerview.itemAnimator = null + binding.recyclerview.layoutManager = LinearLayoutManager(this) + binding.recyclerview.adapter = adapter - cancel_textview.setOnClickListener { finish() } + binding.cancelTextview.setOnClickListener { finish() } initFilterViews() } private fun initFilterViews() { - app_filter_info.setOnClickListener { + binding.appFilterInfo.setOnClickListener { scrollToTopAfterCommit = true presenter.disableFilter() - app_filter_info.collapse(true, LinearOutSlowInInterpolator()) + binding.appFilterInfo.collapse(true, LinearOutSlowInInterpolator()) } - TooltipCompat.setTooltipText(filter_imageview, filter_imageview.contentDescription) - filter_imageview.setOnClickListener { + TooltipCompat.setTooltipText(binding.filterImageview, binding.filterImageview.contentDescription) + binding.filterImageview.setOnClickListener { presenter.updateFilter(null) toggleFilterView() } - filter_edittext.apply { + binding.filterEdittext.apply { filters = arrayOf(InputFilter { source, _, _, _, _, _ -> if (source.isEmpty() || source.matches("^[a-zA-Z0-9._*]*$".toRegex())) { null @@ -91,60 +93,51 @@ class AppsActivity : BaseActivity(R.layout.activity_apps, true), AppsContract.Ap } }) setOnEditorActionListener { _, actionId, _ -> - val filterString = filter_edittext.text.toString().trim() + val filterString = text.toString().trim() if (actionId == EditorInfo.IME_ACTION_DONE && filterString.isNotEmpty() && !presenter.duplicateFilter( filterString ) ) { presenter.addAppFilter(filterString) addFilterChip(filterString) - filter_edittext.text.clear() + text.clear() } true } - addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(s: Editable?) { - presenter.updateFilter(s) - setCompoundDrawables(null, null, if (!s.isNullOrEmpty()) clearDrawable else null, null) - } - - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { - } - - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - } - }) - setOnTouchListener { v, event -> - var consumed = false - if (v is EditText) { - if (event.x >= v.width - v.totalPaddingRight) { - if (event.action == MotionEvent.ACTION_UP) { - filter_edittext.text.clear() - } - consumed = true + doAfterTextChanged { + presenter.updateFilter(it) + setCompoundDrawables(null, null, if (!it.isNullOrEmpty()) clearDrawable else null, null) + } + setOnTouchListener { _, event -> + if (event.x >= width - totalPaddingRight) { + if (event.action == MotionEvent.ACTION_UP) { + performClick() + text.clear() } + return@setOnTouchListener true } - consumed + false } } // necessary to measure the view heights for animations setFilterChips(presenter.getCurrentFilter()) - filter_linearlayout.doOnPreDraw { it.visibility = View.GONE } - app_filter_info.doOnPreDraw { it.visibility = View.GONE } + binding.filterLinearlayout.doOnPreDraw { it.visibility = View.GONE } + binding.appFilterInfo.doOnPreDraw { it.visibility = View.GONE } } override fun toggleResultView(installedAppPackages: List, filters: List) { adapter.initialList(installedAppPackages, filters) - show_all_textview.text = getString(R.string.show_all_apps).format(installedAppPackages.size) - app_filter_info.visibility = if (filters.isEmpty() || installedAppPackages.isEmpty()) View.GONE else View.VISIBLE - if (filter_edittext.text.toString().isNotEmpty()) { - adapter.updateAppFilter(filter_edittext.text.toString()) + binding.showAllTextview.text = getString(R.string.show_all_apps).format(installedAppPackages.size) + binding.appFilterInfo.visibility = + if (filters.isEmpty() || installedAppPackages.isEmpty()) View.GONE else View.VISIBLE + if (binding.filterEdittext.text.toString().isNotEmpty()) { + adapter.updateAppFilter(binding.filterEdittext.text.toString()) } - ViewCompat.animate(progressbar).alpha(0f) - .setDuration(resources.getInteger(android.R.integer.config_shortAnimTime).toLong()).withEndAction { - progressbar.visibility = View.INVISIBLE - }.start() + ViewCompat.animate(binding.progressbar).alpha(0f) + .setDuration(resources.getInteger(android.R.integer.config_shortAnimTime).toLong()) + .withEndAction { binding.progressbar.visibility = View.INVISIBLE } + .start() } override fun updateAppFilter(filters: List) = adapter.updateAppFilters(filters) @@ -153,43 +146,43 @@ class AppsActivity : BaseActivity(R.layout.activity_apps, true), AppsContract.Ap override fun updateFilterIcon(filterActive: Boolean) { if (filterActive) { - filter_imageview.setColorFilter(ResourcesCompat.getColor(resources, R.color.colorAccent, null)) + binding.filterImageview.setColorFilter(ResourcesCompat.getColor(resources, R.color.colorAccent, null)) } else { - filter_imageview.clearColorFilter() + binding.filterImageview.clearColorFilter() } } private fun toggleFilterView() { - if (filter_linearlayout.isVisible) { - if (presenter.getCurrentFilter().isNotEmpty()) app_filter_info.expand(true, FastOutLinearInInterpolator()) - filter_linearlayout.collapse(easing = FastOutLinearInInterpolator()) - filter_edittext.hideKeyboard() + if (binding.filterLinearlayout.isVisible) { + if (presenter.getCurrentFilter().isNotEmpty()) binding.appFilterInfo.expand(true, FastOutLinearInInterpolator()) + binding.filterLinearlayout.collapse(easing = FastOutLinearInInterpolator()) + binding.filterEdittext.hideKeyboard() } else { - if (app_filter_info.isVisible) app_filter_info.collapse(true, LinearOutSlowInInterpolator()) - filter_edittext.text.clear() + if (binding.appFilterInfo.isVisible) binding.appFilterInfo.collapse(true, LinearOutSlowInInterpolator()) + binding.filterEdittext.text.clear() setFilterChips(presenter.getCurrentFilter()) - filter_linearlayout.expand(easing = LinearOutSlowInInterpolator()) + binding.filterLinearlayout.expand(easing = LinearOutSlowInInterpolator()) } } private fun removeAppFilter(chip: Chip) { - chip_group.removeView(chip) + binding.chipGroup.removeView(chip) scrollToTopAfterCommit = true - presenter.removeAppFilter(chip.text.toString(), filter_edittext.text.toString()) + presenter.removeAppFilter(chip.text.toString(), binding.filterEdittext.text.toString()) } private fun setFilterChips(filters: Collection) { - chip_group.removeAllViews() + binding.chipGroup.removeAllViews() for (i in filters) { addFilterChip(i) } } private fun addFilterChip(filterString: String) { - (LayoutInflater.from(this).inflate(R.layout.filter_chip, chip_group, false) as Chip).let { + (LayoutInflater.from(this).inflate(R.layout.filter_chip, binding.chipGroup, false) as Chip).let { it.text = filterString it.isClickable = false - chip_group.addView(it) + binding.chipGroup.addView(it) it.setOnCloseIconClickListener { view -> removeAppFilter(view as Chip) } } } @@ -203,7 +196,7 @@ class AppsActivity : BaseActivity(R.layout.activity_apps, true), AppsContract.Ap @Suppress("DEPRECATION") setColorFilter(ResourcesCompat.getColor(resources, R.color.iconTintColor, null), PorterDuff.Mode.SRC_IN) } - setBounds(0, 0, this.intrinsicWidth, this.intrinsicHeight) + setBounds(0, 0, intrinsicWidth, intrinsicHeight) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivityModule.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivityModule.kt index d0a46fd8..56d702d2 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivityModule.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsActivityModule.kt @@ -10,7 +10,7 @@ import dagger.Module import dagger.Provides import javax.inject.Named -@Module(includes = [ActivityModule::class]) +@Module(includes = [ActivityModule::class, WidgetIDModule::class]) abstract class AppsActivityModule { @Binds @@ -28,17 +28,16 @@ abstract class AppsActivityModule { @Binds @ActivityScope abstract fun provideAppInfoBuilder(appInfoBuilder: AppInfo.AppInfoBuilderImpl): AppInfo.AppInfoBuilder +} - @Module - companion object { - - @JvmStatic - @Provides - @ActivityScope - @Named(WIDGET_ID) - fun provideWidgetId(activity: AppsActivity): String { - return (activity.intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) - ?: AppWidgetManager.INVALID_APPWIDGET_ID).toString() - } +@Module +object WidgetIDModule { + + @Provides + @ActivityScope + @Named(WIDGET_ID) + fun provideWidgetId(activity: AppsActivity): String { + return (activity.intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) + ?: AppWidgetManager.INVALID_APPWIDGET_ID).toString() } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsAdapter.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsAdapter.kt index b5893a14..12ca4a3f 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsAdapter.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsAdapter.kt @@ -7,35 +7,37 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.g00fy2.developerwidget.R +import com.g00fy2.developerwidget.activities.appmanager.AppsAdapter.AppViewHolder import com.g00fy2.developerwidget.base.BaseAdapter import com.g00fy2.developerwidget.base.BaseViewHolder +import com.g00fy2.developerwidget.databinding.AppItemBinding import com.g00fy2.developerwidget.ktx.filterPackageName -import kotlinx.android.synthetic.main.app_item.* -class AppsAdapter : BaseAdapter(AppsDiffUtilsCallback()) { +class AppsAdapter : BaseAdapter(AppsDiffUtilsCallback()) { private var onAppClicked: ((AppInfo?) -> Unit) = {} private var itemsCopy = ArrayList() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { - return BaseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.app_item, parent, false)).apply { - addRipple() - itemView.setOnClickListener { - onAppClicked(getSelectedPackageName(adapterPosition)) + inner class AppViewHolder(val binding: AppItemBinding) : BaseViewHolder(binding) { + override fun onBind(item: AppInfo) { + item.run { + binding.appenameTextview.text = appName + binding.apppackageTextview.text = packageName + binding.appVersionTextview.text = + String.format(itemView.context.getString(R.string.apk_version), versionName, versionCode) + binding.appIconImageview.setImageDrawable(appIcon) + if (VERSION.SDK_INT >= VERSION_CODES.O) { + binding.appIconImageview.setBackgroundResource(if (appIcon is InsetDrawable) R.drawable.bg_adaptive_launcher_icon else 0) + } } } } - override fun onBindViewHolder(holderApk: BaseViewHolder, position: Int) { - holderApk.apply { - val appInfo = getItem(position) - appename_textview.text = appInfo.appName - apppackage_textview.text = appInfo.packageName - app_version_textview.text = - String.format(itemView.context.getString(R.string.apk_version), appInfo.versionName, appInfo.versionCode) - app_icon_imageview.setImageDrawable(appInfo.appIcon) - if (VERSION.SDK_INT >= VERSION_CODES.O) { - app_icon_imageview.setBackgroundResource(if (appInfo.appIcon is InsetDrawable) R.drawable.bg_adaptive_launcher_icon else 0) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder { + return AppViewHolder(AppItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)).apply { + addRipple() + itemView.setOnClickListener { + onAppClicked(getSelectedPackageName(adapterPosition)) } } } @@ -79,5 +81,4 @@ class AppsAdapter : BaseAdapter(AppsDiffUtilsCallback() submitList(itemsCopy) } } - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsPresenterImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsPresenterImpl.kt index 05e8cc95..ceff7583 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsPresenterImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/appmanager/AppsPresenterImpl.kt @@ -4,6 +4,7 @@ import android.content.pm.ApplicationInfo import android.text.Editable import androidx.lifecycle.Lifecycle.Event import androidx.lifecycle.OnLifecycleEvent +import androidx.lifecycle.lifecycleScope import com.g00fy2.developerwidget.base.BasePresenterImpl import com.g00fy2.developerwidget.controllers.IntentController import com.g00fy2.developerwidget.controllers.WidgetPreferenceController @@ -32,7 +33,7 @@ class AppsPresenterImpl @Inject constructor() : BasePresenterImpl(), AppsContrac @OnLifecycleEvent(Event.ON_RESUME) fun scanApps() { - launch { + view.lifecycleScope.launch { withContext(Dispatchers.IO) { getInstalledUserApps() }.let { diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/CreateShortcutActivity.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/CreateShortcutActivity.kt index d940154f..7aa08ea5 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/CreateShortcutActivity.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/CreateShortcutActivity.kt @@ -9,7 +9,6 @@ import android.graphics.Canvas import android.graphics.drawable.Icon import android.os.Build.VERSION import android.os.Build.VERSION_CODES -import android.os.Bundle import android.widget.LinearLayout import androidx.annotation.DrawableRes import androidx.annotation.RequiresApi @@ -17,42 +16,58 @@ import androidx.annotation.StringRes import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.getSystemService import androidx.core.graphics.drawable.toBitmap +import androidx.core.view.updatePadding import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewbinding.ViewBinding import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.activities.apkinstall.ApkActivity import com.g00fy2.developerwidget.activities.appmanager.AppsActivity import com.g00fy2.developerwidget.base.BaseActivity import com.g00fy2.developerwidget.base.BaseContract.BasePresenter -import kotlinx.android.synthetic.main.activity_create_shortcut.* +import com.g00fy2.developerwidget.databinding.ActivityCreateShortcutBinding +import com.g00fy2.developerwidget.ktx.doOnApplyWindowInsets import javax.inject.Inject @RequiresApi(VERSION_CODES.N_MR1) -class CreateShortcutActivity : BaseActivity(R.layout.activity_create_shortcut), - CreateShortcutContract.CreateShortcutView { +class CreateShortcutActivity : BaseActivity(), CreateShortcutContract.CreateShortcutView { @Inject lateinit var presenter: CreateShortcutContract.CreateShortcutPresenter + private lateinit var binding: ActivityCreateShortcutBinding private lateinit var adapter: ShortcutAdapter private lateinit var shortcutInfoList: List override fun providePresenter(): BasePresenter = presenter - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + override fun setViewBinding(): ViewBinding { + binding = ActivityCreateShortcutBinding.inflate(layoutInflater) + return binding + } + + override fun initView() { setResult(Activity.RESULT_CANCELED) adapter = ShortcutAdapter() - recyclerview.setHasFixedSize(true) - recyclerview.layoutManager = LinearLayoutManager(this) - recyclerview.adapter = adapter + binding.recyclerview.setHasFixedSize(true) + binding.recyclerview.layoutManager = LinearLayoutManager(this) + binding.recyclerview.adapter = adapter getDrawable(R.drawable.divider_line)?.let { - recyclerview.addItemDecoration(DividerItemDecoration(this, LinearLayout.VERTICAL).apply { setDrawable(it) }) + binding.recyclerview.addItemDecoration( + DividerItemDecoration( + this, + LinearLayout.VERTICAL + ).apply { setDrawable(it) }) } shortcutInfoList = generateShortcutInfoList() adapter.submitList(shortcutInfoList) adapter.setOnShortcutSelected { shortcutPosition -> onItemClick(shortcutPosition) } + if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) { + binding.recyclerview.doOnApplyWindowInsets { view, insets, padding, _ -> + view.updatePadding(bottom = padding.bottom + insets.systemWindowInsetBottom) + } + } } private fun onItemClick(position: Int) { diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/ShortcutAdapter.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/ShortcutAdapter.kt index 71a735fc..c020832e 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/ShortcutAdapter.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/shortcut/ShortcutAdapter.kt @@ -5,27 +5,28 @@ import android.os.Build.VERSION_CODES import android.view.LayoutInflater import android.view.ViewGroup import androidx.annotation.RequiresApi -import com.g00fy2.developerwidget.R +import com.g00fy2.developerwidget.activities.shortcut.ShortcutAdapter.ShortcutViewHolder import com.g00fy2.developerwidget.base.BaseAdapter import com.g00fy2.developerwidget.base.BaseViewHolder -import kotlinx.android.synthetic.main.shortcut_item.* +import com.g00fy2.developerwidget.databinding.ShortcutItemBinding -class ShortcutAdapter : BaseAdapter(ShortcutDiffUtilsCallback()) { +@RequiresApi(VERSION_CODES.N_MR1) +class ShortcutAdapter : BaseAdapter(ShortcutDiffUtilsCallback()) { private var onShortcutSelected: ((Int) -> Unit) = {} - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { - return BaseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.shortcut_item, parent, false)).apply { - addRipple() - itemView.setOnClickListener { onShortcutSelected(adapterPosition) } + inner class ShortcutViewHolder(val binding: ShortcutItemBinding) : BaseViewHolder(binding) { + override fun onBind(item: ShortcutInfo) { + item.run { + binding.shortcutTitleTextview.text = item.longLabel ?: item.shortLabel ?: "" + } } } - @RequiresApi(VERSION_CODES.N_MR1) - override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { - holder.apply { - shortcut_title_textview.text = - getItem(position)?.longLabel?.toString() ?: getItem(position)?.shortLabel.toString() + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShortcutViewHolder { + return ShortcutViewHolder(ShortcutItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)).apply { + addRipple() + itemView.setOnClickListener { onShortcutSelected(adapterPosition) } } } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/DeviceDataAdapter.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/DeviceDataAdapter.kt index ac6f4db3..e58e73e4 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/DeviceDataAdapter.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/DeviceDataAdapter.kt @@ -3,30 +3,43 @@ package com.g00fy2.developerwidget.activities.widgetconfig import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.base.BaseAdapter import com.g00fy2.developerwidget.base.BaseViewHolder import com.g00fy2.developerwidget.data.DeviceDataItem -import kotlinx.android.synthetic.main.device_data_header_item.* -import kotlinx.android.synthetic.main.device_data_value_item.* +import com.g00fy2.developerwidget.databinding.DeviceDataHeaderItemBinding +import com.g00fy2.developerwidget.databinding.DeviceDataValueItemBinding -class DeviceDataAdapter : BaseAdapter, BaseViewHolder>(DeviceDataDiffUtilsCallback()) { +class DeviceDataAdapter : + BaseAdapter, BaseViewHolder>>(DeviceDataDiffUtilsCallback()) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + inner class DeviceDataHeaderViewHolder(val binding: DeviceDataHeaderItemBinding) : + BaseViewHolder>(binding) { + override fun onBind(item: Pair) { + item.run { + binding.headerDividerView.visibility = if (adapterPosition == 0) View.INVISIBLE else View.VISIBLE + binding.headerTitleTextview.text = itemView.context.getString(second.title) + } + } + } + + inner class DeviceDataValueViewHolder(val binding: DeviceDataValueItemBinding) : + BaseViewHolder>(binding) { + override fun onBind(item: Pair) { + item.run { + binding.deviceDataTitle.text = itemView.context.getString(second.title) + binding.deviceData.text = second.value + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder> { return when (viewType) { - HEADER_TYPE -> BaseViewHolder( - LayoutInflater.from(parent.context).inflate( - R.layout.device_data_header_item, - parent, - false - ) + HEADER_TYPE -> DeviceDataHeaderViewHolder( + DeviceDataHeaderItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) - VALUE_TYPE -> BaseViewHolder( - LayoutInflater.from(parent.context).inflate( - R.layout.device_data_value_item, - parent, - false - ) + VALUE_TYPE + -> DeviceDataValueViewHolder( + DeviceDataValueItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) else -> { throw IllegalStateException("Unknown ViewType") @@ -34,23 +47,6 @@ class DeviceDataAdapter : BaseAdapter, BaseViewHold } } - override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { - holder.apply { - getItem(position).let { - when (holder.itemViewType) { - HEADER_TYPE -> { - header_divider_view.visibility = if (position == 0) View.INVISIBLE else View.VISIBLE - header_title_textview.text = itemView.context.getString(it.second.title) - } - VALUE_TYPE -> { - device_data_title.text = itemView.context.getString(it.second.title) - device_data.text = it.second.value - } - } - } - } - } - override fun getItemViewType(position: Int) = if (getItem(position).second.isHeader) HEADER_TYPE else VALUE_TYPE companion object { diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivity.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivity.kt index cc7506c3..f957436e 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivity.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivity.kt @@ -8,6 +8,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.res.Resources import android.graphics.BlendMode import android.graphics.BlendModeColorFilter import android.graphics.PorterDuff @@ -15,7 +16,6 @@ import android.graphics.Rect import android.graphics.drawable.Drawable import android.os.Build.VERSION import android.os.Build.VERSION_CODES -import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.MotionEvent @@ -26,28 +26,33 @@ import android.webkit.WebView import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.getSystemService import androidx.core.content.res.ResourcesCompat -import androidx.core.view.marginBottom +import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewbinding.ViewBinding import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.activities.about.AboutActivity import com.g00fy2.developerwidget.base.BaseActivity import com.g00fy2.developerwidget.base.BaseContract.BasePresenter import com.g00fy2.developerwidget.data.DeviceDataItem +import com.g00fy2.developerwidget.databinding.ActivityWidgetConfigBinding +import com.g00fy2.developerwidget.ktx.doOnApplyWindowInsets +import com.g00fy2.developerwidget.ktx.gesturalNavigationMode import com.g00fy2.developerwidget.ktx.hideKeyboard import com.g00fy2.developerwidget.ktx.showKeyboard +import com.g00fy2.developerwidget.ktx.updateMargin import com.g00fy2.developerwidget.receiver.widget.WidgetProviderImpl -import kotlinx.android.synthetic.main.activity_widget_config.* import javax.inject.Inject -class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), WidgetConfigContract.WidgetConfigView { +class WidgetConfigActivity : BaseActivity(), WidgetConfigContract.WidgetConfigView { @Inject lateinit var presenter: WidgetConfigContract.WidgetConfigPresenter + private lateinit var binding: ActivityWidgetConfigBinding + private lateinit var adapter: DeviceDataAdapter private var updateExistingWidget = false private var launchedFromAppLauncher = true private var widgetId: Int = AppWidgetManager.INVALID_APPWIDGET_ID - private lateinit var adapter: DeviceDataAdapter private val editDrawable by lazy { initEditDrawable() } private val closeConfigureActivityReceiver by lazy { @@ -61,8 +66,12 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg override fun providePresenter(): BasePresenter = presenter - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + override fun setViewBinding(): ViewBinding { + binding = ActivityWidgetConfigBinding.inflate(layoutInflater) + return binding + } + + override fun initView() { setResult(Activity.RESULT_CANCELED) intent.extras?.let { @@ -76,23 +85,27 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg return } - setActionbarElevationListener(widget_config_root_scrollview) - widget_config_root_scrollview.viewTreeObserver.addOnScrollChangedListener { - val scrollableRange = - widget_config_root_scrollview.computeVerticalScrollRange() - widget_config_root_scrollview.height - val fabOffset = (share_fab.height / 2) + share_fab.marginBottom - if (widget_config_root_scrollview.scrollY < scrollableRange - fabOffset) { - share_fab.hide() - } else { - share_fab.show() + setActionbarElevationListener(binding.widgetConfigRootScrollview) + binding.widgetConfigRootScrollview.apply { + viewTreeObserver.addOnScrollChangedListener { + val scrollableRange = getChildAt(0).bottom - height + paddingBottom + val fabOffset = binding.shareFab.height + (12 * resources.displayMetrics.density).toInt() + if (scrollY < scrollableRange - fabOffset) { + binding.shareFab.hide() + } else { + binding.shareFab.show() + } + if (VERSION.SDK_INT >= VERSION_CODES.O_MR1 && !gesturalNavigationMode()) { + clipToPadding = (scrollY >= scrollableRange) + } } } adapter = DeviceDataAdapter() - recyclerview.setHasFixedSize(false) - recyclerview.layoutManager = LinearLayoutManager(this) - recyclerview.isNestedScrollingEnabled = false - recyclerview.adapter = adapter + binding.recyclerview.setHasFixedSize(false) + binding.recyclerview.layoutManager = LinearLayoutManager(this) + binding.recyclerview.isNestedScrollingEnabled = false + binding.recyclerview.adapter = adapter // register broadcast receiver to finish activity after pin widget if (VERSION.SDK_INT >= VERSION_CODES.O) { @@ -100,14 +113,41 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg } // set up webview pre oreo to get implementation information if (VERSION.SDK_INT in VERSION_CODES.LOLLIPOP until VERSION_CODES.O) { - WebView(this) + try { + WebView(this) + } catch (e: Resources.NotFoundException) { + WebView(applicationContext) + } + } + + binding.deviceTitleEdittextview.apply { + onFocusChangeListener = OnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + presenter.setCustomDeviceName(binding.deviceTitleEdittextview.text.toString()) + toggleDeviceNameEdit(false) + } + } + setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + clearFocus() + } + true + } + } + binding.shareFab.setOnClickListener { presenter.shareDeviceData() } + if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) { + binding.widgetConfigRootScrollview.doOnApplyWindowInsets { view, insets, padding, _ -> + view.updatePadding(bottom = padding.bottom + insets.systemWindowInsetBottom) + } + binding.shareFab.doOnApplyWindowInsets { view, insets, _, margin -> + view.updateMargin(bottom = margin.bottom + insets.systemWindowInsetBottom) + } } - initViews() } override fun onResume() { super.onResume() - initViews() + updateWidgetButton() } override fun onDestroy() { @@ -132,24 +172,24 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg override fun showDeviceData(data: List>) = adapter.submitList(data) override fun setDeviceTitle(title: String) { - device_title_textview.text = title - device_title_textview.visibility = View.VISIBLE - device_title_edittextview.setText(title) - device_title_edittextview.setSelection(device_title_edittextview.text.length) + binding.deviceTitleTextview.text = title + binding.deviceTitleTextview.visibility = View.VISIBLE + binding.deviceTitleEdittextview.setText(title) + binding.deviceTitleEdittextview.setSelection(binding.deviceTitleEdittextview.text.length) } override fun setDeviceTitleHint(hint: String) { - device_title_edittextview.hint = hint + binding.deviceTitleEdittextview.hint = hint } override fun setSubtitle(data: Pair) { - device_subtitle_textview.text = getString(R.string.subtitle).format(data.first, data.second) + binding.deviceSubtitleTextview.text = getString(R.string.subtitle).format(data.first, data.second) } override fun dispatchTouchEvent(event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_DOWN) { currentFocus.let { - if (it != null && it == device_title_edittextview) { + if (it != null && it == binding.deviceTitleEdittextview) { Rect().let { rect -> it.getGlobalVisibleRect(rect) if (!rect.contains(event.rawX.toInt(), event.rawY.toInt())) { @@ -162,10 +202,10 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg return super.dispatchTouchEvent(event) } - private fun initViews() { + private fun updateWidgetButton() { val showAddWidget = (!launchedFromAppLauncher || (widgetCount() < 1 && isPinAppWidgetSupported())) if (showAddWidget) { - apply_button.apply { + binding.applyButton.apply { visibility = View.VISIBLE when { launchedFromAppLauncher -> { @@ -183,9 +223,9 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg } } } else { - apply_button.visibility = View.GONE + binding.applyButton.visibility = View.GONE } - device_title_textview.apply { + binding.deviceTitleTextview.apply { setOnClickListener { toggleDeviceNameEdit(true) } if (showAddWidget) { isClickable = true @@ -202,36 +242,21 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg setPadding(paddingLeft, paddingTop, (16 * resources.displayMetrics.density).toInt(), paddingBottom) } } - device_title_edittextview.apply { - onFocusChangeListener = OnFocusChangeListener { _, hasFocus -> - if (!hasFocus) { - presenter.setCustomDeviceName(device_title_edittextview.text.toString()) - toggleDeviceNameEdit(false) - } - } - setOnEditorActionListener { v, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_DONE) { - v.clearFocus() - } - true - } - } - share_fab.setOnClickListener { presenter.shareDeviceData() } } private fun toggleDeviceNameEdit(editable: Boolean) { - device_title_textview.visibility = if (editable) View.INVISIBLE else View.VISIBLE - device_title_edittextview.visibility = if (editable) View.VISIBLE else View.INVISIBLE + binding.deviceTitleTextview.visibility = if (editable) View.INVISIBLE else View.VISIBLE + binding.deviceTitleEdittextview.visibility = if (editable) View.VISIBLE else View.INVISIBLE if (editable) { - device_title_edittextview.requestFocus() - device_title_edittextview.showKeyboard() + binding.deviceTitleEdittextview.requestFocus() + binding.deviceTitleEdittextview.showKeyboard() } else { - device_title_edittextview.hideKeyboard() + binding.deviceTitleEdittextview.hideKeyboard() } } private fun updateWidgetAndFinish(existing: Boolean) { - presenter.setCustomDeviceName(device_title_edittextview.text.toString(), true) + presenter.setCustomDeviceName(binding.deviceTitleEdittextview.text.toString(), true) if (!existing) { setResult(Activity.RESULT_OK, Intent().apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId) }) } @@ -252,7 +277,7 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg this, 0, Intent(applicationContext, WidgetProviderImpl::class.java).apply { action = WidgetProviderImpl.UPDATE_WIDGET_MANUALLY_ACTION putExtra(EXTRA_APPWIDGET_FROM_PIN_APP, true) - putExtra(EXTRA_APPWIDGET_CUSTOM_DEVICE_NAME, device_title_edittextview.text.toString()) + putExtra(EXTRA_APPWIDGET_CUSTOM_DEVICE_NAME, binding.deviceTitleEdittextview.text.toString()) }, PendingIntent.FLAG_UPDATE_CURRENT ) @@ -267,13 +292,8 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg if (!supported) presenter.showManuallyAddWidgetNotice() } - private fun isPinAppWidgetSupported(): Boolean { - return if (VERSION.SDK_INT >= VERSION_CODES.O) { - getSystemService()?.isRequestPinAppWidgetSupported ?: false - } else { - false - } - } + private fun isPinAppWidgetSupported() = + VERSION.SDK_INT >= VERSION_CODES.O && getSystemService()?.isRequestPinAppWidgetSupported ?: false private fun widgetCount() = AppWidgetManager.getInstance(this).getAppWidgetIds( ComponentName( @@ -291,7 +311,7 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg @Suppress("DEPRECATION") setColorFilter(ResourcesCompat.getColor(resources, R.color.iconTintColor, null), PorterDuff.Mode.SRC_IN) } - setBounds(0, 0, this.intrinsicWidth, this.intrinsicHeight) + setBounds(0, 0, intrinsicWidth, intrinsicHeight) alpha = 128 } } @@ -302,4 +322,4 @@ class WidgetConfigActivity : BaseActivity(R.layout.activity_widget_config), Widg const val EXTRA_APPWIDGET_FROM_PIN_APP = "EXTRA_APPWIDGET_FROM_PIN_APP" const val EXTRA_APPWIDGET_CUSTOM_DEVICE_NAME = "EXTRA_APPWIDGET_CUSTOM_DEVICE_NAME" } -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivityModule.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivityModule.kt index 082a7339..facaea1f 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivityModule.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigActivityModule.kt @@ -10,7 +10,7 @@ import dagger.Module import dagger.Provides import javax.inject.Named -@Module(includes = [ActivityModule::class]) +@Module(includes = [ActivityModule::class, WidgetIDModule::class]) abstract class WidgetConfigActivityModule { @Binds @@ -24,17 +24,16 @@ abstract class WidgetConfigActivityModule { @Binds @ActivityScope abstract fun provideWidgetConfigPresenter(presenter: WidgetConfigPresenterImpl): WidgetConfigContract.WidgetConfigPresenter +} - @Module - companion object { +@Module +object WidgetIDModule { - @JvmStatic - @Provides - @ActivityScope - @Named(WIDGET_ID) - fun provideWidgetId(activity: WidgetConfigActivity): String { - return (activity.intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) - ?: AppWidgetManager.INVALID_APPWIDGET_ID).toString() - } + @Provides + @ActivityScope + @Named(WIDGET_ID) + fun provideWidgetId(activity: WidgetConfigActivity): String { + return (activity.intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) + ?: AppWidgetManager.INVALID_APPWIDGET_ID).toString() } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigPresenterImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigPresenterImpl.kt index e0a1a97e..4f5e4050 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigPresenterImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/activities/widgetconfig/WidgetConfigPresenterImpl.kt @@ -2,6 +2,7 @@ package com.g00fy2.developerwidget.activities.widgetconfig import androidx.lifecycle.Lifecycle.Event import androidx.lifecycle.OnLifecycleEvent +import androidx.lifecycle.lifecycleScope import com.g00fy2.developerwidget.R import com.g00fy2.developerwidget.base.BasePresenterImpl import com.g00fy2.developerwidget.controllers.IntentController @@ -35,7 +36,7 @@ class WidgetConfigPresenterImpl @Inject constructor() : BasePresenterImpl(), @OnLifecycleEvent(Event.ON_CREATE) override fun loadDeviceData() { - launch { + view.lifecycleScope.launch { withContext(Dispatchers.IO) { getDeviceData() }.let { @@ -46,7 +47,7 @@ class WidgetConfigPresenterImpl @Inject constructor() : BasePresenterImpl(), @OnLifecycleEvent(Event.ON_CREATE) override fun loadCustomDeviceName() { - launch { + view.lifecycleScope.launch { withContext(Dispatchers.IO) { widgetPreferenceController.getCustomDeviceName() }.let { @@ -80,7 +81,7 @@ class WidgetConfigPresenterImpl @Inject constructor() : BasePresenterImpl(), override fun showHomescreen() = intentController.showHomescreen() private suspend fun getDeviceData(): List> { - return deviceDataSource + val itemList = deviceDataSource .getStaticDeviceData() .plus(deviceDataSource.getHardwareData()) .plus(deviceDataSource.getSoftwareInfo()) @@ -93,10 +94,13 @@ class WidgetConfigPresenterImpl @Inject constructor() : BasePresenterImpl(), { !it.second.isHeader }, { stringController.getString(it.second.title) }) ) + + val emptyCategories = itemList.groupingBy { it.second.category }.eachCount().filter { it.value > 1 } + return itemList.filter { emptyCategories.containsKey(it.second.category) } } override fun shareDeviceData() { - launch { + view.lifecycleScope.launch { withContext(Dispatchers.IO) { getDeviceData() }.let { intentController.shareDeviceData(formatDeviceDataString(it)) } @@ -113,6 +117,5 @@ class WidgetConfigPresenterImpl @Inject constructor() : BasePresenterImpl(), }.removeSurrounding("\n") } - override fun showManuallyAddWidgetNotice() = toastController.showToast(R.string.manually_add_widget) } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseActivity.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseActivity.kt index 5fa4d5af..d3543620 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseActivity.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseActivity.kt @@ -7,37 +7,40 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import android.view.Window -import androidx.annotation.LayoutRes -import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.updatePadding +import androidx.viewbinding.ViewBinding import com.g00fy2.developerwidget.controllers.DayNightController -import dagger.android.AndroidInjection +import com.g00fy2.developerwidget.ktx.doOnApplyWindowInsets +import dagger.android.support.DaggerAppCompatActivity import timber.log.Timber import javax.inject.Inject -abstract class BaseActivity(@LayoutRes contentLayoutId: Int, private val isDialogActivity: Boolean = false) : - AppCompatActivity(contentLayoutId) { +abstract class BaseActivity(private val isDialogActivity: Boolean = false) : DaggerAppCompatActivity() { @Inject lateinit var dayNightController: DayNightController override fun onCreate(savedInstanceState: Bundle?) { - AndroidInjection.inject(this) Timber.d("Lifecycle: %s1 onCreate %s2", localClassName, hashCode()) if (isDialogActivity) { requestWindowFeature(Window.FEATURE_NO_TITLE) super.onCreate(savedInstanceState) + setContentView(setViewBinding().root) val width = (resources.displayMetrics.widthPixels * 0.95).toInt() val height = (resources.displayMetrics.heightPixels * 0.80).toInt() window.setLayout(width, height) } else { super.onCreate(savedInstanceState) + setContentView(setViewBinding().root) } dayNightController.loadCustomDefaultMode() lifecycle.addObserver(providePresenter()) initCompatNavigationBar() + initView() + if (!isDialogActivity) initGestureNavigation() } override fun onDestroy() { @@ -46,6 +49,15 @@ abstract class BaseActivity(@LayoutRes contentLayoutId: Int, private val isDialo lifecycle.removeObserver(providePresenter()) } + // TODO check if there will be a fix for https://issuetracker.google.com/issues/139738913 in AppCompatActivity or next Android release + override fun onBackPressed() { + if (VERSION.SDK_INT == VERSION_CODES.Q && isTaskRoot && supportFragmentManager.backStackEntryCount == 0) { + finishAfterTransition() + } else { + super.onBackPressed() + } + } + protected fun setActionbarElevationListener(viewGroup: ViewGroup) { supportActionBar?.elevation = 0f viewGroup.viewTreeObserver.addOnScrollChangedListener { @@ -61,12 +73,28 @@ abstract class BaseActivity(@LayoutRes contentLayoutId: Int, private val isDialo } } + private fun initGestureNavigation() { + if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) { + window.decorView.let { + it.systemUiVisibility.let { flags -> + it.systemUiVisibility = + flags or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + } + } + findViewById(Window.ID_ANDROID_CONTENT)?.let { + it.doOnApplyWindowInsets { view, insets, padding, _ -> + view.updatePadding(top = padding.top + insets.systemWindowInsetTop) + } + } + } + } + private fun initCompatNavigationBar() { // api 27+ allow applying flag via xml (windowLightNavigationBar) if (VERSION.SDK_INT == VERSION_CODES.O && isInNightMode()) { - window.decorView.let { view -> - view.systemUiVisibility.let { flags -> - view.systemUiVisibility = flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + window.decorView.let { + it.systemUiVisibility.let { flags -> + it.systemUiVisibility = flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR } } } @@ -77,4 +105,8 @@ abstract class BaseActivity(@LayoutRes contentLayoutId: Int, private val isDialo resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_NO abstract fun providePresenter(): BaseContract.BasePresenter + + abstract fun setViewBinding(): ViewBinding + + abstract fun initView() } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseAdapter.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseAdapter.kt index 6b7b9599..a92a2dc7 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseAdapter.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseAdapter.kt @@ -2,9 +2,8 @@ package com.g00fy2.developerwidget.base import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -abstract class BaseAdapter constructor(diffCallback: DiffUtil.ItemCallback) : +abstract class BaseAdapter> constructor(diffCallback: DiffUtil.ItemCallback) : ListAdapter(diffCallback) { private var commitCallback: Runnable? = null @@ -13,6 +12,10 @@ abstract class BaseAdapter constructor(diffCall submitList(list, commitCallback) } + override fun onBindViewHolder(holder: VH, position: Int) { + holder.onBind(getItem(position)) + } + open fun clearList() { submitList(null) } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseContract.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseContract.kt index 4185ee44..bca0099e 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseContract.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseContract.kt @@ -1,11 +1,11 @@ package com.g00fy2.developerwidget.base import androidx.lifecycle.LifecycleObserver -import kotlinx.coroutines.CoroutineScope +import androidx.lifecycle.LifecycleOwner interface BaseContract { - interface BaseView + interface BaseView : LifecycleOwner - interface BasePresenter : LifecycleObserver, CoroutineScope + interface BasePresenter : LifecycleObserver } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BasePresenterImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BasePresenterImpl.kt index a3740e9d..93a53133 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BasePresenterImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BasePresenterImpl.kt @@ -1,18 +1,3 @@ package com.g00fy2.developerwidget.base -import androidx.lifecycle.Lifecycle.Event -import androidx.lifecycle.OnLifecycleEvent -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlin.coroutines.CoroutineContext - -abstract class BasePresenterImpl : BaseContract.BasePresenter { - - private val job: Job by lazy { Job() } - - override val coroutineContext: CoroutineContext = Dispatchers.Main + job - - @OnLifecycleEvent(Event.ON_DESTROY) - fun cancelJob() = job.cancel() - -} \ No newline at end of file +abstract class BasePresenterImpl : BaseContract.BasePresenter \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseViewHolder.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseViewHolder.kt index 9363b274..929b71a9 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseViewHolder.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/base/BaseViewHolder.kt @@ -1,20 +1,12 @@ package com.g00fy2.developerwidget.base -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES -import android.util.TypedValue -import android.view.View import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer +import androidx.viewbinding.ViewBinding +import com.g00fy2.developerwidget.ktx.addRipple -open class BaseViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer { +abstract class BaseViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) { - fun addRipple() { - TypedValue().apply { - containerView.context.theme.resolveAttribute(android.R.attr.selectableItemBackground, this, true) - }.resourceId.let { - if (VERSION.SDK_INT < VERSION_CODES.M) itemView.setBackgroundResource(it) else itemView.foreground = - containerView.context.getDrawable(it) - } - } + abstract fun onBind(item: T) + + fun addRipple() = itemView.addRipple(true) } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/DayNightControllerImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/DayNightControllerImpl.kt index 5691a5dd..4310446b 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/DayNightControllerImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/DayNightControllerImpl.kt @@ -15,7 +15,7 @@ import javax.inject.Named class DayNightControllerImpl @Inject constructor() : DayNightController { @Inject - @field:Named(APPLICATION) + @Named(APPLICATION) lateinit var context: Context @Inject lateinit var toastController: ToastController diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/IntentControllerImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/IntentControllerImpl.kt index 69181dab..6d00d830 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/IntentControllerImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/IntentControllerImpl.kt @@ -20,7 +20,7 @@ import javax.inject.Named class IntentControllerImpl @Inject constructor() : IntentController { @Inject - @field:Named(ACTIVITY) + @Named(ACTIVITY) lateinit var context: Context @Inject lateinit var toastController: ToastController diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/PermissionControllerImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/PermissionControllerImpl.kt index f27c946f..a9340d52 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/PermissionControllerImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/PermissionControllerImpl.kt @@ -14,7 +14,7 @@ import javax.inject.Named class PermissionControllerImpl @Inject constructor() : PermissionController { @Inject - @field:Named(ACTIVITY) + @Named(ACTIVITY) lateinit var context: Context override fun hasPermission(permission: String): Boolean { @@ -45,5 +45,4 @@ class PermissionControllerImpl @Inject constructor() : PermissionController { override fun requestPermissions(permissions: Array) { if (!hasPermissions(permissions)) (context as Activity).requestPermissions(permissions, 1) } - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StorageDirsControllerImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StorageDirsControllerImpl.kt index 4dc20d65..ad0571dd 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StorageDirsControllerImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StorageDirsControllerImpl.kt @@ -18,7 +18,7 @@ import javax.inject.Named class StorageDirsControllerImpl @Inject constructor() : StorageDirsController { @Inject - @field:Named(ACTIVITY) + @Named(ACTIVITY) lateinit var context: Context /** diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StringControllerImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StringControllerImpl.kt index aa2df2f8..52ca07ad 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StringControllerImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/StringControllerImpl.kt @@ -8,9 +8,8 @@ import javax.inject.Named class StringControllerImpl @Inject constructor() : StringController { @Inject - @field:Named(ACTIVITY) + @Named(ACTIVITY) lateinit var context: Context override fun getString(resId: Int): String = context.getString(resId) - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/ToastControllerImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/ToastControllerImpl.kt index e345a95e..3d423498 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/ToastControllerImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/ToastControllerImpl.kt @@ -9,7 +9,7 @@ import javax.inject.Named class ToastControllerImpl @Inject constructor() : ToastController { @Inject - @field:Named(APPLICATION) + @Named(APPLICATION) lateinit var context: Context private var toast: Toast? = null diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/WidgetPreferenceControllerImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/WidgetPreferenceControllerImpl.kt index dc566074..b7b247a5 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/WidgetPreferenceControllerImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/controllers/WidgetPreferenceControllerImpl.kt @@ -10,10 +10,10 @@ import javax.inject.Named class WidgetPreferenceControllerImpl @Inject constructor() : WidgetPreferenceController { @Inject - @field:Named(ACTIVITY) + @Named(ACTIVITY) lateinit var context: Context @Inject - @field:Named(WIDGET_ID) + @Named(WIDGET_ID) lateinit var widgetId: String private val sharedPreference by lazy { diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataItem.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataItem.kt index 7c960467..ab2cbcd7 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataItem.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataItem.kt @@ -12,13 +12,13 @@ class DeviceDataItem(value: String, @StringRes title: Int, category: Category, i var category = category; private set var isHeader = isHeader; private set - enum class Category(val title: String, @StringRes val titleRes: Int) { - DEVICE("Device", R.string.device_title), - SYSTEM("System", R.string.system_title), - CPU("CPU", R.string.cpu_title), - MEMORY("Memory", R.string.memory_title), - DISPLAY("Display", R.string.display_title), - FEATURES("Features", R.string.features_title), - SOFTWARE("Software", R.string.software_title) + enum class Category(@StringRes val titleRes: Int) { + DEVICE(R.string.device_title), + SYSTEM(R.string.system_title), + CPU(R.string.cpu_title), + MEMORY(R.string.memory_title), + DISPLAY(R.string.display_title), + FEATURES(R.string.features_title), + SOFTWARE(R.string.software_title) } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataSourceImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataSourceImpl.kt index a11b1462..70c280b4 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataSourceImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/data/DeviceDataSourceImpl.kt @@ -17,7 +17,7 @@ import javax.inject.Named class DeviceDataSourceImpl @Inject constructor() : DeviceDataSource { @Inject - @field:Named(APPLICATION) + @Named(APPLICATION) lateinit var context: Context override suspend fun getStaticDeviceData(): Map { @@ -98,7 +98,7 @@ class DeviceDataSourceImpl @Inject constructor() : DeviceDataSource { override suspend fun getHeaderItems(): Map { val data = HashMap() Category.values().forEach { - data[it.title] = DeviceDataItem(it, true) + data[it.name] = DeviceDataItem(it, true) } return data } diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/data/WidgetsPreferencesDataSourceImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/data/WidgetsPreferencesDataSourceImpl.kt index 2641f032..99ed443d 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/data/WidgetsPreferencesDataSourceImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/data/WidgetsPreferencesDataSourceImpl.kt @@ -4,23 +4,15 @@ import android.content.Context import android.util.SparseArray import com.g00fy2.developerwidget.controllers.WidgetPreferenceControllerImpl import com.g00fy2.developerwidget.di.annotations.APPLICATION -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import javax.inject.Inject import javax.inject.Named -import kotlin.coroutines.CoroutineContext -class WidgetsPreferencesDataSourceImpl @Inject constructor() : WidgetsPreferencesDataSource, CoroutineScope { +class WidgetsPreferencesDataSourceImpl @Inject constructor() : WidgetsPreferencesDataSource { @Inject - @field:Named(APPLICATION) + @Named(APPLICATION) lateinit var context: Context - private val job: Job by lazy { Job() } - - override val coroutineContext: CoroutineContext = Dispatchers.Main + job - override suspend fun getCustomDeviceNames(widgetIds: IntArray): SparseArray { val customDeviceNames = SparseArray() for (widgetId in widgetIds) { diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/data/device/systemapps/SystemAppsDataProvider.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/data/device/systemapps/SystemAppsDataProvider.kt index eef35b63..0a3c398e 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/data/device/systemapps/SystemAppsDataProvider.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/data/device/systemapps/SystemAppsDataProvider.kt @@ -21,25 +21,27 @@ class SystemAppsDataProvider { } } - @SuppressLint("PrivateApi") + @SuppressLint("PrivateApi", "WebViewApiAvailability") fun getWebViewImplementation(context: Context): String { return when { - VERSION.SDK_INT >= VERSION_CODES.O -> WebView.getCurrentWebViewPackage()?.let { - context.packageManager.getApplicationLabel( - it.applicationInfo - ).toString() + " " + it.versionName - } ?: "" + VERSION.SDK_INT >= VERSION_CODES.O -> { + WebView.getCurrentWebViewPackage()?.let { + context.packageManager.getApplicationLabel(it.applicationInfo).toString() + " " + it.versionName + } ?: "" + } VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP -> { try { (Class.forName("android.webkit.WebViewFactory") .getMethod("getLoadedPackageInfo") - .invoke(null) as PackageInfo?)?.let { context.packageManager.getApplicationLabel(it.applicationInfo).toString() + " " + it.versionName } + .invoke(null) as PackageInfo?)?.let { + context.packageManager.getApplicationLabel(it.applicationInfo).toString() + " " + it.versionName + } ?: "" } catch (t: Throwable) { "" } } - Version(VERSION.RELEASE.toString()).isAtLeast("4.4.3") -> "WebView v33.0.0.0" + Version(VERSION.RELEASE).isAtLeast("4.4.3") -> "WebView v33.0.0.0" VERSION.SDK_INT >= VERSION_CODES.KITKAT -> "WebView v30.0.0.0" else -> try { context.packageManager.getPackageInfo("com.google.android.webview", 0) diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/di/AppModule.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/di/AppModule.kt index 5e4c9683..d5684723 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/di/AppModule.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/di/AppModule.kt @@ -15,5 +15,4 @@ abstract class AppModule { @Singleton @Named(APPLICATION) abstract fun provideApplicationContext(application: DevWidgetApp): Context - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/di/BroadcastBindingModule.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/di/BroadcastBindingModule.kt index 091eecf0..fecee317 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/di/BroadcastBindingModule.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/di/BroadcastBindingModule.kt @@ -1,14 +1,12 @@ package com.g00fy2.developerwidget.di import com.g00fy2.developerwidget.receiver.widget.WidgetProviderImpl -import com.g00fy2.developerwidget.receiver.widget.WidgetProviderModule import dagger.Module import dagger.android.ContributesAndroidInjector @Module abstract class BroadcastBindingModule { - @ContributesAndroidInjector(modules = [WidgetProviderModule::class]) + @ContributesAndroidInjector abstract fun bindWidgetProvider(): WidgetProviderImpl - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/di/ControllerModule.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/di/ControllerModule.kt index 20525d8f..ec318ff2 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/di/ControllerModule.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/di/ControllerModule.kt @@ -53,5 +53,4 @@ abstract class ActivityControllerModule { @Binds @ActivityScope abstract fun provideStorageDirsController(storageDirsControllerImpl: StorageDirsControllerImpl): StorageDirsController - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ActivityExtension.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ActivityExtension.kt new file mode 100644 index 00000000..de3ba401 --- /dev/null +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ActivityExtension.kt @@ -0,0 +1,13 @@ +package com.g00fy2.developerwidget.ktx + +import android.app.Activity +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES + +fun Activity.gesturalNavigationMode(): Boolean { + return if (VERSION.SDK_INT >= VERSION_CODES.Q) { + window.decorView.rootWindowInsets.systemGestureInsets.left > 0 + } else { + false + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/AppInfoExtensions.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/AppInfoExtensions.kt index 05538688..64861d5a 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/AppInfoExtensions.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/AppInfoExtensions.kt @@ -22,4 +22,4 @@ fun AppInfo.filterPackageName(filterEntries: Collection): Boolean { if (filterPackageName(i)) return true } return false -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ViewExtension.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ViewExtension.kt new file mode 100644 index 00000000..b49dcb11 --- /dev/null +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ViewExtension.kt @@ -0,0 +1,64 @@ +package com.g00fy2.developerwidget.ktx + +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import android.view.WindowInsets +import androidx.annotation.Px +import androidx.annotation.RequiresApi +import androidx.core.view.marginBottom +import androidx.core.view.marginLeft +import androidx.core.view.marginRight +import androidx.core.view.marginTop + +fun View.addRipple(asForeground: Boolean = false) { + TypedValue().apply { context.theme.resolveAttribute(android.R.attr.selectableItemBackground, this, true) } + .resourceId.let { + if (VERSION.SDK_INT < VERSION_CODES.M || !asForeground) setBackgroundResource(it) else foreground = + context.getDrawable(it) + } +} + +fun View.updateMargin( + @Px left: Int = marginLeft, + @Px top: Int = marginTop, + @Px right: Int = marginRight, + @Px bottom: Int = marginBottom +) { + val params = layoutParams as ViewGroup.MarginLayoutParams + params.leftMargin = left + params.topMargin = top + params.rightMargin = right + params.bottomMargin = bottom + layoutParams = params +} + +@RequiresApi(VERSION_CODES.KITKAT_WATCH) +fun View.doOnApplyWindowInsets(f: (View, WindowInsets, InitialPadding, InitialMargin) -> Unit) { + val initialPadding = recordInitialPaddingForView(this) + val initialMargin = recordInitialMarginForView(this) + setOnApplyWindowInsetsListener { v, insets -> + f(v, insets, initialPadding, initialMargin) + insets + } +} + +data class InitialPadding( + val left: Int, val top: Int, + val right: Int, val bottom: Int +) + +data class InitialMargin( + val left: Int, val top: Int, + val right: Int, val bottom: Int +) + +private fun recordInitialPaddingForView(view: View) = InitialPadding( + view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom +) + +private fun recordInitialMarginForView(view: View) = InitialMargin( + view.marginLeft, view.marginTop, view.marginRight, view.marginBottom +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ViewGroupExtensions.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ViewGroupExtensions.kt index f3e054ff..d730f1ac 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ViewGroupExtensions.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/ktx/ViewGroupExtensions.kt @@ -4,48 +4,53 @@ import android.animation.TimeInterpolator import android.animation.ValueAnimator import android.view.View import android.view.ViewGroup +import android.view.animation.AccelerateDecelerateInterpolator import androidx.core.animation.doOnEnd import com.g00fy2.developerwidget.R -fun ViewGroup.expand(fade: Boolean = false, easing: TimeInterpolator? = null) = this.apply { - val params = layoutParams as ViewGroup.MarginLayoutParams - params.topMargin = -height - requestLayout() - if (fade) alpha = 0f - visibility = View.VISIBLE +fun ViewGroup.expand(fade: Boolean = false, easing: TimeInterpolator = AccelerateDecelerateInterpolator()) { + this.apply { + val params = layoutParams as ViewGroup.MarginLayoutParams + params.topMargin = -height + layoutParams = params + if (fade) alpha = 0f + visibility = View.VISIBLE - val viewHeight = height - ValueAnimator.ofInt(-viewHeight, 0).apply { - addUpdateListener { - (it.animatedValue as Int).let { value -> - params.topMargin = value - if (fade) alpha = (viewHeight + value) / viewHeight.toFloat() + val viewHeight = height + ValueAnimator.ofInt(-viewHeight, 0).apply { + addUpdateListener { + (it.animatedValue as Int).let { value -> + params.topMargin = value + layoutParams = params + if (fade) alpha = (viewHeight + value) / viewHeight.toFloat() + } } - requestLayout() - } - easing?.let { easing -> interpolator = easing } - duration = resources.getInteger(R.integer.animation_duration).toLong() - }.start() + interpolator = easing + duration = resources.getInteger(R.integer.animation_duration).toLong() + }.start() + } } -fun ViewGroup.collapse(fade: Boolean = false, easing: TimeInterpolator? = null) = this.apply { - val params = layoutParams as ViewGroup.MarginLayoutParams - params.topMargin = 0 - requestLayout() - if (fade) alpha = 1f - visibility = View.VISIBLE +fun ViewGroup.collapse(fade: Boolean = false, easing: TimeInterpolator = AccelerateDecelerateInterpolator()) { + this.apply { + val params = layoutParams as ViewGroup.MarginLayoutParams + params.topMargin = 0 + layoutParams = params + if (fade) alpha = 1f + visibility = View.VISIBLE - val viewHeight = height - ValueAnimator.ofInt(0, -viewHeight).apply { - addUpdateListener { - (it.animatedValue as Int).let { value -> - params.topMargin = value - if (fade) alpha = (viewHeight + value) / viewHeight.toFloat() + val viewHeight = height + ValueAnimator.ofInt(0, -viewHeight).apply { + addUpdateListener { + (it.animatedValue as Int).let { value -> + params.topMargin = value + layoutParams = params + if (fade) alpha = (viewHeight + value) / viewHeight.toFloat() + } } - requestLayout() - } - easing?.let { easing -> interpolator = easing } - duration = resources.getInteger(R.integer.animation_duration).toLong() - doOnEnd { visibility = View.GONE } - }.start() + interpolator = easing + duration = resources.getInteger(R.integer.animation_duration).toLong() + doOnEnd { visibility = View.GONE } + }.start() + } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderContract.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderContract.kt deleted file mode 100644 index e0a57542..00000000 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderContract.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.g00fy2.developerwidget.receiver.widget - -import android.util.SparseArray -import com.g00fy2.developerwidget.data.DeviceDataItem - -interface WidgetProviderContract { - - interface WidgetProvider { - - fun updateWidgetData( - appWidgetIds: IntArray, - data: Map, - customDeviceNames: SparseArray - ) - } - - interface WidgetProviderPresenter { - - fun getDeviceData(widgetIDs: IntArray) - - fun saveCustomDeviceName(widgetId: Int, customDeviceName: String): Boolean - - fun clearWidgetPreferences(widgetId: Int) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderImpl.kt index ccdd4507..68963522 100644 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderImpl.kt +++ b/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderImpl.kt @@ -19,17 +19,23 @@ import com.g00fy2.developerwidget.activities.widgetconfig.WidgetConfigActivity import com.g00fy2.developerwidget.controllers.DayNightController import com.g00fy2.developerwidget.controllers.DayNightControllerImpl import com.g00fy2.developerwidget.data.DeviceDataItem +import com.g00fy2.developerwidget.data.DeviceDataSource import com.g00fy2.developerwidget.data.DeviceDataSourceImpl -import com.g00fy2.developerwidget.receiver.widget.WidgetProviderContract.WidgetProvider -import com.g00fy2.developerwidget.receiver.widget.WidgetProviderContract.WidgetProviderPresenter +import com.g00fy2.developerwidget.data.WidgetsPreferencesDataSource import dagger.android.AndroidInjection +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject -class WidgetProviderImpl : AppWidgetProvider(), WidgetProvider { +class WidgetProviderImpl : AppWidgetProvider() { @Inject - lateinit var presenter: WidgetProviderPresenter + lateinit var deviceDataSource: DeviceDataSource + @Inject + lateinit var widgetsPreferencesDataSource: WidgetsPreferencesDataSource @Inject lateinit var dayNightController: DayNightController @@ -46,7 +52,7 @@ class WidgetProviderImpl : AppWidgetProvider(), WidgetProvider { val widgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID val customDeviceName = intent.extras?.getString(WidgetConfigActivity.EXTRA_APPWIDGET_CUSTOM_DEVICE_NAME) ?: "" if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID && customDeviceName.isNotBlank()) { - presenter.saveCustomDeviceName(widgetId, customDeviceName) + widgetsPreferencesDataSource.saveCustomDeviceName(widgetId, customDeviceName) } context.sendBroadcast(Intent(WidgetConfigActivity.EXTRA_APPWIDGET_CLOSE_CONFIGURE)) } @@ -65,15 +71,22 @@ class WidgetProviderImpl : AppWidgetProvider(), WidgetProvider { } } - override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) = - presenter.getDeviceData(appWidgetIds) + override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { + GlobalScope.launch { + withContext(Dispatchers.IO) { + val data = deviceDataSource.getStaticDeviceData() + val customDeviceNames = widgetsPreferencesDataSource.getCustomDeviceNames(appWidgetIds) + updateWidgetData(appWidgetIds, data, customDeviceNames) + } + } + } override fun onDeleted(context: Context?, appWidgetIds: IntArray) { Timber.d("onDeleted widget %s", appWidgetIds.first()) - presenter.clearWidgetPreferences(appWidgetIds.first()) + widgetsPreferencesDataSource.clearWidgetPreferences(appWidgetIds.first()) } - override fun updateWidgetData( + private fun updateWidgetData( appWidgetIds: IntArray, data: Map, customDeviceNames: SparseArray @@ -144,4 +157,4 @@ class WidgetProviderImpl : AppWidgetProvider(), WidgetProvider { companion object { const val UPDATE_WIDGET_MANUALLY_ACTION = BuildConfig.APPLICATION_ID + ".APPWIDGET_MANUAL_UPDATE" } -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderModule.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderModule.kt deleted file mode 100644 index 64adfc44..00000000 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderModule.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.g00fy2.developerwidget.receiver.widget - -import com.g00fy2.developerwidget.receiver.widget.WidgetProviderContract.WidgetProvider -import com.g00fy2.developerwidget.receiver.widget.WidgetProviderContract.WidgetProviderPresenter -import dagger.Binds -import dagger.Module - -@Module -abstract class WidgetProviderModule { - - @Binds - abstract fun provideWidgetProvider(widgetProviderImpl: WidgetProviderImpl): WidgetProvider - - @Binds - abstract fun provideWidgetProviderPresenter(presenter: WidgetProviderPresenterImpl): WidgetProviderPresenter -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderPresenterImpl.kt b/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderPresenterImpl.kt deleted file mode 100644 index fb867c28..00000000 --- a/app/src/main/kotlin/com/g00fy2/developerwidget/receiver/widget/WidgetProviderPresenterImpl.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.g00fy2.developerwidget.receiver.widget - -import com.g00fy2.developerwidget.data.DeviceDataSource -import com.g00fy2.developerwidget.data.WidgetsPreferencesDataSource -import com.g00fy2.developerwidget.receiver.widget.WidgetProviderContract.WidgetProvider -import com.g00fy2.developerwidget.receiver.widget.WidgetProviderContract.WidgetProviderPresenter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import javax.inject.Inject -import kotlin.coroutines.CoroutineContext - -class WidgetProviderPresenterImpl @Inject constructor() : WidgetProviderPresenter, CoroutineScope { - - @Inject - lateinit var widgetProvider: WidgetProvider - @Inject - lateinit var deviceDataSource: DeviceDataSource - @Inject - lateinit var widgetsPreferencesDataSource: WidgetsPreferencesDataSource - - private val job: Job by lazy { Job() } - - override val coroutineContext: CoroutineContext = Dispatchers.Main + job - - override fun getDeviceData(widgetIDs: IntArray) { - launch { - withContext(Dispatchers.IO) { - val data = deviceDataSource.getStaticDeviceData() - val customDeviceNames = widgetsPreferencesDataSource.getCustomDeviceNames(widgetIDs) - widgetProvider.updateWidgetData(widgetIDs, data, customDeviceNames) - } - } - } - - override fun saveCustomDeviceName(widgetId: Int, customDeviceName: String) = - widgetsPreferencesDataSource.saveCustomDeviceName(widgetId, customDeviceName) - - override fun clearWidgetPreferences(widgetId: Int) = widgetsPreferencesDataSource.clearWidgetPreferences(widgetId) -} \ No newline at end of file diff --git a/app/src/main/res/drawable-night/ic_about.xml b/app/src/main/res/drawable-night/ic_about.xml index 85057128..9771b2e1 100644 --- a/app/src/main/res/drawable-night/ic_about.xml +++ b/app/src/main/res/drawable-night/ic_about.xml @@ -4,9 +4,9 @@ android:viewportWidth="24" android:width="24dp"> \ No newline at end of file diff --git a/app/src/main/res/drawable-night/ic_check.xml b/app/src/main/res/drawable-night/ic_check.xml new file mode 100644 index 00000000..b3b91ba2 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_check.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/ic_clear.xml b/app/src/main/res/drawable-night/ic_clear.xml new file mode 100644 index 00000000..f775ee90 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_clear.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/ic_delete.xml b/app/src/main/res/drawable-night/ic_delete.xml new file mode 100644 index 00000000..86f608cc --- /dev/null +++ b/app/src/main/res/drawable-night/ic_delete.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/ic_sad.xml b/app/src/main/res/drawable-night/ic_sad.xml index f5ae6da9..bd4c41aa 100644 --- a/app/src/main/res/drawable-night/ic_sad.xml +++ b/app/src/main/res/drawable-night/ic_sad.xml @@ -1,4 +1,5 @@ + android:pathData="M227.496 327.992c-13.254 0-24-10.742-24-24v-32c0-4.418 3.582-8 8-8s8 3.582 8 8v32c0 4.422 3.582 8 8 8s8-3.578 8-8v-32c0-4.418 3.582-8 8-8s8 3.582 8 8v32c0 13.258-10.746 24-24 24zm-42.742-152c-3.234 0-6.152-1.949-7.391-4.938-1.238-2.988-0.555-6.43 1.734-8.715l58.742-58.746c3.141-3.031 8.129-2.988 11.215 0.098 3.086 3.086 3.129 8.074 0.098 11.215l-58.742 58.742c-1.5 1.504-3.535 2.344-5.656 2.344zm-32.008 32c-3.234 0-6.152-1.949-7.391-4.938-1.238-2.988-0.555-6.43 1.734-8.715l16-16c3.137-3.035 8.129-2.988 11.215 0.094 3.086 3.086 3.129 8.078 0.098 11.215l-16 16c-1.5 1.504-3.535 2.344-5.656 2.344zM99.664 362.906c-2.125 0-4.16-0.844-5.664-2.344-31.184-31.266-31.184-81.871 0-113.137l29.09-29.086c3.137-3.035 8.129-2.988 11.215 0.094 3.086 3.086 3.129 8.078 0.098 11.215l-29.082 29.09c-24.949 25.012-24.949 65.5 0 90.512 2.289 2.289 2.973 5.727 1.734 8.719-1.238 2.988-4.156 4.938-7.391 4.938zm0 0" + tools:ignore="VectorPath"/> diff --git a/app/src/main/res/drawable/bg_widget_inner_shape.xml b/app/src/main/res/drawable/bg_widget_inner_shape.xml index 669b3370..8df00f71 100644 --- a/app/src/main/res/drawable/bg_widget_inner_shape.xml +++ b/app/src/main/res/drawable/bg_widget_inner_shape.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_widget_inner_shape_day.xml b/app/src/main/res/drawable/bg_widget_inner_shape_day.xml index 1db58967..44ab65b9 100644 --- a/app/src/main/res/drawable/bg_widget_inner_shape_day.xml +++ b/app/src/main/res/drawable/bg_widget_inner_shape_day.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_widget_inner_shape_night.xml b/app/src/main/res/drawable/bg_widget_inner_shape_night.xml index d29e2e71..e9061b42 100644 --- a/app/src/main/res/drawable/bg_widget_inner_shape_night.xml +++ b/app/src/main/res/drawable/bg_widget_inner_shape_night.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_changes_logo.xml b/app/src/main/res/drawable/ic_changes_logo.xml index 528e5728..e874dc78 100644 --- a/app/src/main/res/drawable/ic_changes_logo.xml +++ b/app/src/main/res/drawable/ic_changes_logo.xml @@ -1,9 +1,11 @@ + android:pathData="M256 0C114.615 0 0 114.615 0 256s114.615 256 256 256 256-114.615 256-256S397.385 0 256 0zM74.325 162.731c4.851-5.773 12.001-9.114 19.541-9.131h102.4c7.908-0.001 15.374 3.652 20.224 9.899l28.757 36.864-32.512 41.728-26.453-34.133c-1.719-2.126-4.352-3.3-7.083-3.157H94.805c-12.879 0.355-24.117-8.679-26.539-21.333-1.138-7.469 1.079-15.058 6.059-20.737zm142.166 185.77c-4.85 6.246-12.316 9.9-20.224 9.899h-102.4c-14.137 0.206-25.764-11.087-25.97-25.224-0.023-1.584 0.101-3.167 0.37-4.728 2.458-12.621 13.686-21.61 26.539-21.248H179.2c2.626 0.014 5.113-1.182 6.741-3.243l109.227-140.459c4.924-6.342 12.537-10.007 20.565-9.899h54.101v-13.568c-0.008-9.426 7.627-17.073 17.053-17.08 4.555-0.004 8.923 1.814 12.131 5.048l39.765 39.68c6.669 6.661 6.675 17.467 0.013 24.136l-0.013 0.013-39.765 39.765c-6.637 6.692-17.443 6.737-24.136 0.1-3.256-3.229-5.075-7.632-5.048-12.217V204.8H332.8c-2.626-0.014-5.113 1.182-6.741 3.243L216.491 348.501zm222.293-4.181l-0.427-0.085L399.019 384c-6.638 6.692-17.443 6.737-24.136 0.099-3.234-3.208-5.052-7.576-5.048-12.131V358.4h-54.101c-7.908 0.001-15.373-3.652-20.224-9.899l-28.757-36.864 32.512-41.728 26.453 34.133c1.719 2.126 4.352 3.3 7.083 3.157h37.376v-14.763c-0.007-9.426 7.628-17.073 17.053-17.08 4.513-0.004 8.845 1.781 12.046 4.963l39.851 39.851c6.575 6.754 6.429 17.559-0.325 24.134l-0.018 0.016z" + tools:ignore="VectorPath"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_github_logo_shape.xml b/app/src/main/res/drawable/ic_github_logo_shape.xml index 463dae98..bdad04a5 100644 --- a/app/src/main/res/drawable/ic_github_logo_shape.xml +++ b/app/src/main/res/drawable/ic_github_logo_shape.xml @@ -1,9 +1,11 @@ + android:pathData="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14 0.954 8.945 0.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-0.571-0.049-5.708-0.144-15.417-0.098-9.709-0.144-18.179-0.144-25.406l-6.567 1.136c-4.187 0.767-9.469 1.092-15.846 1-6.374-0.089-12.991-0.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-0.572-1.335-0.098-2.43 1.427-3.289 1.525-0.859 4.281-1.276 8.28-1.276l5.708 0.853c3.807 0.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-0.476 16.274-1.423 4.565-0.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-0.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-0.01-39.771-9.818-76.454-29.414-110.049z" + tools:ignore="VectorPath"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_sad.xml b/app/src/main/res/drawable/ic_sad.xml index 5d07ddfb..c8e21c2e 100644 --- a/app/src/main/res/drawable/ic_sad.xml +++ b/app/src/main/res/drawable/ic_sad.xml @@ -1,4 +1,5 @@ + android:pathData="M227.496 327.992c-13.254 0-24-10.742-24-24v-32c0-4.418 3.582-8 8-8s8 3.582 8 8v32c0 4.422 3.582 8 8 8s8-3.578 8-8v-32c0-4.418 3.582-8 8-8s8 3.582 8 8v32c0 13.258-10.746 24-24 24zm-42.742-152c-3.234 0-6.152-1.949-7.391-4.938-1.238-2.988-0.555-6.43 1.734-8.715l58.742-58.746c3.141-3.031 8.129-2.988 11.215 0.098 3.086 3.086 3.129 8.074 0.098 11.215l-58.742 58.742c-1.5 1.504-3.535 2.344-5.656 2.344zm-32.008 32c-3.234 0-6.152-1.949-7.391-4.938-1.238-2.988-0.555-6.43 1.734-8.715l16-16c3.137-3.035 8.129-2.988 11.215 0.094 3.086 3.086 3.129 8.078 0.098 11.215l-16 16c-1.5 1.504-3.535 2.344-5.656 2.344zM99.664 362.906c-2.125 0-4.16-0.844-5.664-2.344-31.184-31.266-31.184-81.871 0-113.137l29.09-29.086c3.137-3.035 8.129-2.988 11.215 0.094 3.086 3.086 3.129 8.078 0.098 11.215l-29.082 29.09c-24.949 25.012-24.949 65.5 0 90.512 2.289 2.289 2.973 5.727 1.734 8.719-1.238 2.988-4.156 4.938-7.391 4.938zm0 0" + tools:ignore="VectorPath"/> diff --git a/app/src/main/res/drawable/ic_twitter_logo.xml b/app/src/main/res/drawable/ic_twitter_logo.xml index 16e58b79..0a531ea3 100644 --- a/app/src/main/res/drawable/ic_twitter_logo.xml +++ b/app/src/main/res/drawable/ic_twitter_logo.xml @@ -1,9 +1,11 @@ + android:pathData="M24.826 0C11.137 0 0 11.137 0 24.826c0 13.688 11.137 24.826 24.826 24.826 13.688 0 24.826-11.138 24.826-24.826C49.652 11.137 38.516 0 24.826 0zm11.075 19.144c0.011 0.246 0.017 0.494 0.017 0.742 0 7.551-5.746 16.255-16.259 16.255-3.227 0-6.231-0.943-8.759-2.565 0.447 0.053 0.902 0.08 1.363 0.08 2.678 0 5.141-0.914 7.097-2.446-2.5-0.046-4.611-1.698-5.338-3.969 0.348 0.066 0.707 0.103 1.074 0.103 0.521 0 1.027-0.068 1.506-0.199-2.614-0.524-4.583-2.833-4.583-5.603l0.001-0.072c0.77 0.427 1.651 0.685 2.587 0.714-1.532-1.023-2.541-2.773-2.541-4.755 0-1.048 0.281-2.03 0.773-2.874 2.817 3.458 7.029 5.732 11.777 5.972-0.098-0.419-0.147-0.854-0.147-1.303 0-3.155 2.558-5.714 5.713-5.714 1.644 0 3.127 0.694 4.171 1.804 1.303-0.256 2.523-0.73 3.63-1.387-0.43 1.335-1.333 2.454-2.516 3.162 1.157-0.138 2.261-0.444 3.282-0.899-0.762 1.144-1.731 2.151-2.848 2.954z" + tools:ignore="VectorPath"/> \ No newline at end of file diff --git a/app/src/main/res/layout/about_feedback_dialog.xml b/app/src/main/res/layout/about_feedback_dialog.xml index 2e469b28..2670aa83 100644 --- a/app/src/main/res/layout/about_feedback_dialog.xml +++ b/app/src/main/res/layout/about_feedback_dialog.xml @@ -2,14 +2,14 @@ - @@ -76,4 +74,4 @@ app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/about_item_header.xml b/app/src/main/res/layout/about_item_header.xml index b3062b3e..58fd2c13 100644 --- a/app/src/main/res/layout/about_item_header.xml +++ b/app/src/main/res/layout/about_item_header.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 83c34ec1..e2c05c85 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -6,6 +6,7 @@ android:id="@+id/about_root_scrollview" android:layout_width="match_parent" android:layout_height="match_parent" + android:clipToPadding="false" android:scrollbars="vertical" > @@ -106,10 +107,10 @@ android:layout_height="64dp" android:layout_gravity="center" android:indeterminate="true" - app:layout_constraintBottom_toTopOf="@+id/cancel_textview" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/textView" + app:layout_constraintBottom_toBottomOf="@+id/no_items_imageview" + app:layout_constraintEnd_toEndOf="@+id/no_items_imageview" + app:layout_constraintStart_toStartOf="@+id/no_items_imageview" + app:layout_constraintTop_toTopOf="@+id/no_items_imageview" /> @@ -48,178 +48,180 @@ /> - - + android:background="@color/backgroundColor" + android:orientation="vertical" + > - + - + - - - - + - + + + + + + + - + + - - - - - - - - - - - - - - + + + + + + + + + diff --git a/app/src/main/res/layout/activity_create_shortcut.xml b/app/src/main/res/layout/activity_create_shortcut.xml index 97cf4cfb..a2cb8606 100644 --- a/app/src/main/res/layout/activity_create_shortcut.xml +++ b/app/src/main/res/layout/activity_create_shortcut.xml @@ -8,8 +8,6 @@ android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="8dp" - > - - + android:clipToPadding="false" + /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_widget_config.xml b/app/src/main/res/layout/activity_widget_config.xml index 01fe79a1..fcfc1f88 100644 --- a/app/src/main/res/layout/activity_widget_config.xml +++ b/app/src/main/res/layout/activity_widget_config.xml @@ -1,127 +1,130 @@ - - - - - - - - - - - - - - + > + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/filter_chip.xml b/app/src/main/res/layout/filter_chip.xml index 68b0c61f..aa0cd06b 100644 --- a/app/src/main/res/layout/filter_chip.xml +++ b/app/src/main/res/layout/filter_chip.xml @@ -6,8 +6,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_marginRight="2dp" - android:textAlignment="center" style="@style/Widget.MaterialComponents.Chip.Entry" app:checkedIconEnabled="false" app:chipBackgroundColor="@color/chipColor" + app:closeIconTint="@color/iconTintColor" /> \ No newline at end of file diff --git a/app/src/main/res/menu-v29/configuration_menu.xml b/app/src/main/res/menu-v29/configuration_menu.xml deleted file mode 100644 index 5d67a5d3..00000000 --- a/app/src/main/res/menu-v29/configuration_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/values-night-v29/colors.xml b/app/src/main/res/values-night-v29/colors.xml deleted file mode 100644 index b602b262..00000000 --- a/app/src/main/res/values-night-v29/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - @color/black - @color/black - - #1f1f1f - @color/black - - @color/white - diff --git a/app/src/main/res/values-night-v29/styles.xml b/app/src/main/res/values-night-v29/styles.xml deleted file mode 100644 index 5abf5817..00000000 --- a/app/src/main/res/values-night-v29/styles.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 33170474..953bd2f7 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -4,20 +4,20 @@ @color/nightPrimary @color/nightAccent + @color/nightTextPrimary + @color/nightTextTertiary + @color/nightBackground - #80212121 - #3c3c3c + @color/transparentBackground50_night + #5f6368 - @color/black - @color/black + #A6202124 #a0a0a0 #1ffafafa - @color/white - @color/white - #b3b3b3 + @color/nightTextPrimary + @color/nightTextPrimary @color/nightGrey - #e4e4e5 #33000000 diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 56beb7f1..f689c799 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -7,4 +7,16 @@ false + + + + diff --git a/app/src/main/res/values-v23/colors.xml b/app/src/main/res/values-v23/colors.xml index c13100a8..0b50ed03 100644 --- a/app/src/main/res/values-v23/colors.xml +++ b/app/src/main/res/values-v23/colors.xml @@ -1,4 +1,4 @@ - #e8eaed + @color/colorPrimary diff --git a/app/src/main/res/values-v26/colors.xml b/app/src/main/res/values-v26/colors.xml index 7ee24850..77a1d6f5 100644 --- a/app/src/main/res/values-v26/colors.xml +++ b/app/src/main/res/values-v26/colors.xml @@ -1,4 +1,4 @@ - @color/white + #A6FFFFFF diff --git a/app/src/main/res/values-v29/colors.xml b/app/src/main/res/values-v29/colors.xml index 5a75378c..5400cbc3 100644 --- a/app/src/main/res/values-v29/colors.xml +++ b/app/src/main/res/values-v29/colors.xml @@ -1,7 +1,5 @@ - @color/black - #8ab4f8 - - @color/colorAccent + @color/transparent + @color/transparent diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 78bed956..5afcdcaf 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -3,12 +3,4 @@ false false 300 - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c66038b4..423f71d9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,44 +1,53 @@ - #F8F9FA + #F8F8F8 #999999 @color/originalAccent - #1a73e8 + + @color/dayTextPrimary + @color/dayTextTertiary @color/white @color/white @color/nightBackground - #80ffffff + @color/transparentBackground50_day #80ffffff - #80212121 + #80202124 @color/lighterGrey @color/black - @color/dividerGrey + @color/navigationBarColor @color/black #33303030 - @color/lightGrey - @color/lightGrey - @color/white - @color/lightGrey - @color/lightGrey - @color/white - @color/lightGrey + @color/textTertiary + @color/textTertiary + + @color/textTertiary + + @color/lighterGrey + #33808080 + + #00000000 #ffffff #000000 - #808080 #e0e0e0 + #1a73e8 - @color/black - @color/lighterGrey - #33808080 + #202124 + #5f6368 + #e8eaed + #9aa0a6 + #202124 + @color/nightBackground + #8ab4f8 + #3c4043 - #212121 - #262728 - #2581df - #313235 + @color/dayTextTertiary + @color/dayTextTertiary + @color/nightTextPrimary + @color/nightTextPrimary #1a73e8 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 6472bc42..6e2d40d9 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -6,14 +6,17 @@ @color/colorPrimaryDark @color/colorAccent + @color/textPrimary + @color/textTertiary + @color/backgroundColor @style/ActionBarAccent @style/ActionBarBackground @null @color/colorEdgeEffect + @color/navigationBarColor - @color/navigationBarColor @color/navigationBarDividerColor @@ -37,6 +40,9 @@ @color/colorPrimaryDark @color/colorAccent + @color/textPrimary + @color/textTertiary + true @drawable/bg_dialog_shape false diff --git a/build.gradle b/build.gradle index 4922c7c1..e185f339 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,9 @@ +import com.android.tools.r8.Version +import com.github.jk1.license.filter.* +import com.github.jk1.license.render.* + +apply plugin: 'com.github.jk1.dependency-license-report' + buildscript { ext { // SDK and plugins @@ -5,58 +11,58 @@ buildscript { buildToolsVersion = '29.0.2' minSdkVersion = 14 targetSdkVersion = 29 - androidGradleVersion = '3.6.0-alpha07' - gradleScanVersion = '2.3' - versioningPluginVersion = '1.0.0' + androidGradleVersion = '4.0.0-alpha08' + versioningPluginVersion = '1.0.2' + licenseReportVersion = '1.12' // Library versions - kotlinVersion = '1.3.50' - coroutinesVersion = '1.3.0' - appcompatVersion = '1.1.0-rc01' - coreKtxVersion = '1.2.0-alpha03' - activityVersion = '1.1.0-alpha02' - materialVersion = '1.1.0-alpha09' - recyclerviewVersion = '1.1.0-beta03' - vectorDrawableVersion = '1.1.0-rc01' - constraintlayoutVersion = '2.0.0-beta2' + kotlinVersion = '1.3.61' + coroutinesVersion = '1.3.3' + appcompatVersion = '1.2.0-alpha01' + coreKtxVersion = '1.2.0-rc01' + activityVersion = '1.1.0-rc03' + lifecycleVersion = '2.2.0-rc03' + materialVersion = '1.2.0-alpha03' + recyclerviewVersion = '1.2.0-alpha01' + vectorDrawableVersion = '1.1.0' + constraintlayoutVersion = '2.0.0-beta4' timberVersion = '4.7.1' - daggerVersion = '2.24' - versCompVersion = '1.3.2' + daggerVersion = '2.25.4' + versCompVersion = '1.3.4' } repositories { google() mavenCentral() - jcenter() - maven { url 'https://plugins.gradle.org/m2/' } + jcenter() { + content { + includeModule 'eu.appcom.gradle', 'android-versioning' + includeModule 'org.jetbrains.trove4j', 'trove4j' + } + } + gradlePluginPortal() } dependencies { classpath "com.android.tools.build:gradle:$androidGradleVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "com.gradle:build-scan-plugin:$gradleScanVersion" classpath "eu.appcom.gradle:android-versioning:$versioningPluginVersion" + classpath "com.github.jk1:gradle-license-report:$licenseReportVersion" } } -apply plugin: 'com.gradle.build-scan' - -buildScan { - termsOfServiceUrl = 'https://gradle.com/terms-of-service' - termsOfServiceAgree = 'yes' -} - -allprojects { - repositories { - google() - mavenCentral() - jcenter { - content { - includeModule 'com.g00fy2', 'versioncompare' - includeModule 'org.jetbrains.trove4j', 'trove4j' // required by com.android.tools.lint:lint-gradle - } - } - } +licenseReport { + configurations = ['releaseRuntimeClasspath'] + renderers = [new SimpleHtmlReportRenderer(), new JsonReportRenderer()] + filters = [new LicenseBundleNormalizer()] } task clean(type: Delete) { delete rootProject.buildDir } + +task printR8Version() { + try { + println "R8 version: " + Version.getVersionString() + } catch (ignored) { + println 'R8 version: unknown' + } +} diff --git a/gradle.properties b/gradle.properties index c625a275..b52439ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,26 +1,19 @@ -# Project-wide Gradle settings. +android.useAndroidX=true -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. +# Enable jetifier, currently only because dagger still uses android.support.annotation +android.enableJetifier=true -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html +# Use R8 in full mode instead of ProGuard compatibility mode. +android.enableR8.fullMode=true -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m +# Enable rudimentary R class namespacing where each library only contains +# references to the resources it declares instead of declarations plus all +# transitive dependency references. +android.namespacedRClass=true -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -android.useAndroidX=true -android.enableJetifier=true - -# define kotlin style guide when importing project -kotlin.code.style=official +# Only keep the single relevant constructor for types mentioned in XML files +# instead of using a parameter wildcard which keeps them all. +android.useMinimalKeepRules=true -# enable more aggressive R8 optimizations -android.enableR8.fullMode=true -android.useMinimalKeepRules=true \ No newline at end of file +# Set the build VMs heap size. +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372aef..f3d88b1c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 28f3cbe6..e3185a77 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Jan 01 20:07:12 CET 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-rc-2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 9d82f789..2fe81a7d --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,20 +22,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +64,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +75,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +105,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -105,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` @@ -134,27 +154,30 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282a..9618d8d9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,14 +24,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +62,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +75,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/settings.gradle b/settings.gradle index e7b4def4..0c0a85aa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,12 @@ +plugins { + id 'com.gradle.enterprise' version '3.1.1' +} + +gradleEnterprise { + buildScan { + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' + } +} + include ':app'