targetSdkVersion 31

This commit is contained in:
tateisu 2021-10-28 05:58:19 +09:00
parent d017a47363
commit 9c68857e6e
42 changed files with 2504 additions and 2404 deletions

View File

@ -3,6 +3,11 @@
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8">
<module name="apng" target="1.7" />
<module name="SubwayTooter.apng_android" target="11" />
<module name="SubwayTooter.app" target="11" />
<module name="SubwayTooter.colorpicker" target="11" />
<module name="SubwayTooter.emoji" target="11" />
<module name="SubwayTooter.sample_apng" target="11" />
</bytecodeTargetLevel>
</component>
</project>

View File

@ -4,10 +4,10 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="delegatedBuild" value="false" />
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

View File

@ -2,8 +2,10 @@
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AndroidLintButtonStyle" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintPluralsCandidate" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintSetTextI18n" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="BlockingMethodInNonBlockingContext" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckTagEmptyBody" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ClassName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ConstantConditionIf" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ConvertTwoComparisonsToRangeCheck" enabled="false" level="INFO" enabled_by_default="false" />
@ -23,14 +25,27 @@
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="JoinDeclarationAndAssignment" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JsonStandardCompliance" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="LocalVariableName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="LoopToCallChain" enabled="false" level="INFO" enabled_by_default="false" />
<inspection_tool class="NumericOverflow" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ObjectPropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PrivatePropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="TryFinallyCanBeTryWithResources" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryModuleDependencyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedProperty" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UseWithIndex" enabled="false" level="INFO" enabled_by_default="false" />
<inspection_tool class="XmlDefaultAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDuplicatedId" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlHighlighting" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlUnusedNamespaceDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="YAMLDuplicatedKeys" enabled="false" level="ERROR" enabled_by_default="false" />
</profile>
</component>

View File

@ -11,17 +11,17 @@ repositories {
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'src/lib')
implementation fileTree(include: ['*.jar'], dir: 'src/lib')
implementation "com.google.guava:guava:28.1-jre"
implementation "org.jetbrains.kotlin:kotlin-stdlib"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
def ktor_version="1.5.0"
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-cio:$ktor_version"
implementation "io.ktor:ktor-client-features:$ktor_version"
implementation "io.ktor:ktor-client-encoding:$ktor_version"
def ktorVersion="1.5.0"
implementation "io.ktor:ktor-client-core:$ktorVersion"
implementation "io.ktor:ktor-client-cio:$ktorVersion"
implementation "io.ktor:ktor-client-features:$ktorVersion"
implementation "io.ktor:ktor-client-encoding:$ktorVersion"
// StringEscapeUtils.unescapeHtml4
implementation "org.apache.commons:commons-text:1.9"
@ -33,3 +33,7 @@ dependencies {
test {
useJUnitPlatform()
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@ -30,11 +30,22 @@ android {
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
freeCompilerArgs += [
"-Xopt-in=kotlin.ExperimentalStdlibApi",
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-Xopt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi",
]
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
buildTypes {
release {
@ -57,13 +68,6 @@ android {
}
}
dexOptions {
jumboMode = true
preDexLibraries true
maxProcessCount 10
javaMaxHeapSize "3g"
}
// Generate Signed APK
android.applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
@ -73,7 +77,8 @@ android {
def versionName = defaultConfig.versionName
def flavor = variant.flavorName
def date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
outputFileName = "../../SubwayTooter-${flavor}-${versionCode}-${versionName}-${date}.apk"
def branch = gitBranch()
outputFileName = "../../SubwayTooter-${branch}-${flavor}-${versionCode}-${versionName}-${date}.apk"
}
}
}
@ -81,12 +86,27 @@ android {
packagingOptions {
// https://github.com/Kotlin/kotlinx.coroutines/issues/1064
pickFirst("META-INF/atomicfu.kotlin_module")
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
useLibrary 'android.test.base'
useLibrary 'android.test.mock'
}
static def gitBranch() {
def branch = "(no branch)"
def proc = "git status".execute()
proc.in.eachLine { line ->
def matcher = line =~ /\AOn branch (\S+)/
if (matcher) branch = matcher.group(1)
}
proc.err.eachLine { line -> println line }
proc.waitFor()
branch
}
kapt {
useBuildCache = true
}
@ -109,20 +129,17 @@ dependencies {
implementation "androidx.appcompat:appcompat:$appcompat_version"
//noinspection KtxExtensionAvailable
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// DrawerLayout
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
// NavigationView
implementation "com.google.android.material:material:1.3.0"
implementation "com.google.android.material:material:1.4.0"
// PreferenceManager
implementation "androidx.preference:preference-ktx:1.1.1"
implementation "androidx.exifinterface:exifinterface:1.3.2"
implementation "androidx.exifinterface:exifinterface:1.3.3"
// CustomTabs
implementation "androidx.browser:browser:1.3.0"
@ -155,19 +172,17 @@ dependencies {
testImplementation "junit:junit:$junit_version" // kotlin-testとjunitを併用
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.8.1'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
def okhttpVersion = "4.9.2"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
testImplementation "com.squareup.okhttp3:mockwebserver:$okhttpVersion"
androidTestImplementation "'com.squareup.okhttp3:mockwebserver:$okhttpVersion"
def glideVersion = '4.11.0'
def glideVersion = '4.12.0'
implementation "com.github.bumptech.glide:glide:$glideVersion"
implementation "com.github.bumptech.glide:annotations:$glideVersion"
implementation("com.github.bumptech.glide:okhttp3-integration:$glideVersion") {
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
//
// glide 4.9.0 okhttp3 3.9.1使
// http://bumptech.github.io/glide/int/about.html#how-do-i-use-a-specific-version-of-okhttp-volley-or-other-third-party-library
}
kapt "com.github.bumptech.glide:compiler:$glideVersion"
@ -190,7 +205,7 @@ dependencies {
implementation 'com.astuetz:pagerslidingtabstrip:1.0.1'
implementation 'com.google.android.exoplayer:exoplayer:2.15.0'
implementation 'com.google.android.exoplayer:exoplayer:2.15.1'
/*
WARNING: [Processor] Library '…\exoplayer-ui-2.12.0.aar' contains references to both AndroidX and old support library. This seems like the library is partially migrated. Jetifier will try to rewrite the library anyway.
Example of androidX reference: 'androidx/core/app/NotificationCompat$Builder'
@ -199,6 +214,46 @@ dependencies {
*/
implementation 'com.caverock:androidsvg-aar:1.4'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
// if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// optional - helpers for implementing LifecycleOwner in a Service
implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"
// optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
// optional - ReactiveStreams support for LiveData
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
// optional - Test helpers for LiveData
testImplementation "androidx.arch.core:core-testing:$arch_version"
implementation "com.google.accompanist:accompanist-flowlayout:0.20.0"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.material:material-icons-extended:$compose_version"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation 'androidx.activity:activity-compose:1.4.0-rc01'
}
repositories {

View File

@ -33,6 +33,7 @@ class TestMisskeyMentionAndroid {
// val a="""[[ ]""".toRegex()
// IDEで警告が出るが、Androidは正規表現エンジンが異なるので仕方ない
@Suppress("RegExpRedundantNestedCharacterClass")
assertEquals(true, """[[ ]]][ ]""".toRegex().matches(" ] "))
}

View File

@ -1,7 +1,7 @@
package jp.juggler.subwaytooter
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import jp.juggler.subwaytooter.api.TootApiCallback
import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.api.entity.Host
@ -10,13 +10,13 @@ import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.SimpleHttpClientImpl
import jp.juggler.util.LogCategory
import jp.juggler.util.MySslSocketFactory
import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertNull
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.ConnectionSpec
import okhttp3.OkHttpClient
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
@ -38,10 +38,14 @@ class TestTootInstance {
.readTimeout(60.toLong(), TimeUnit.SECONDS)
.writeTimeout(60.toLong(), TimeUnit.SECONDS)
.pingInterval(10, TimeUnit.SECONDS)
.connectionSpecs(Collections.singletonList(ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.connectionSpecs(
Collections.singletonList(
ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.allEnabledCipherSuites()
.allEnabledTlsVersions()
.build()))
.build()
)
)
.sslSocketFactory(MySslSocketFactory, MySslSocketFactory.trustManager)
.build()
@ -56,7 +60,7 @@ class TestTootInstance {
}
}
private val appContext = InstrumentationRegistry.getTargetContext()!!
private val appContext = InstrumentationRegistry.getInstrumentation().targetContext!!
val client = TootApiClient(
context = appContext,
@ -75,10 +79,10 @@ class TestTootInstance {
runBlocking {
withContext(Dispatchers.IO) {
suspend fun a(host: Host) {
val (ti,ri) = TootInstance.get(client,host )
val (ti, ri) = TootInstance.getEx(client, hostArg = host)
assertNotNull(ti)
assertNull(ri?.error)
ti!!.run{ log.d("${instanceType} ${uri} ${version}")}
ti!!.run { log.d("$instanceType $uri $version") }
}
a(Host.parse("mastodon.juggler.jp"))
@ -92,10 +96,10 @@ class TestTootInstance {
runBlocking {
withContext(Dispatchers.IO) {
suspend fun a(account: SavedAccount) {
val (ti,ri) = TootInstance.get(client,account = account )
val (ti, ri) = TootInstance.getEx(client, account = account)
assertNull(ri?.error)
assertNotNull(ti)
ti!!.run{ log.d("${account.acct} ${instanceType} ${uri} ${version}")}
ti!!.run { log.d("${account.acct} $instanceType $uri $version") }
}
a(SavedAccount(45, "tateisu@mastodon.juggler.jp"))
a(SavedAccount(45, "tateisu@misskey.io", misskeyVersion = 12))

View File

@ -3,19 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="jp.juggler.subwaytooter">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- CAMERAパーミッションをつけるとPlayストアにプライバシーポリシーを記載する必要がある -->
<!--<uses-permission android:name="android.permission.CAMERA"/>-->
<queries>
<!-- (自アプリ以外で)指定URLを開けるアプリの存在確認 -->
<intent>
<action android:name="android.intent.action.VIEW" />
@ -62,6 +50,20 @@
<!-- </intent>-->
</queries>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- CAMERAパーミッションをつけるとPlayストアにプライバシーポリシーを記載する必要がある -->
<!--<uses-permission android:name="android.permission.CAMERA"/>-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".App1"
android:allowBackup="true"
@ -75,30 +77,11 @@
android:theme="@style/AppTheme.Light"
tools:ignore="UnusedAttribute">
<receiver android:name=".EventReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.ACTION_MY_PACKAGE_REPLACED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service
android:name=".notification.PollingService"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name=".notification.PollingForegrounder" />
<activity
android:name=".ActMain"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -108,9 +91,8 @@
<activity
android:name=".ActCallback"
android:label="@string/app_name"
>
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -195,105 +177,122 @@
<activity
android:name=".ActPost"
android:exported="false"
android:label="@string/act_post"
android:windowSoftInputMode="adjustResize">
<!--suppress AndroidElementNotAllowed -->
<layout
android:defaultWidth="320dp"
android:defaultHeight="480dp"
android:defaultWidth="320dp"
android:gravity="center"
android:minWidth="64dp"
android:minHeight="64dp"
android:minWidth="64dp"
tools:ignore="UnusedAttribute" />
</activity>
<activity
android:name=".ActAccountSetting"
android:exported="false"
android:label="@string/account_setting"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActAppSetting"
android:exported="false"
android:label="@string/app_setting"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActColumnList"
android:exported="false"
android:label="@string/column_list"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ActAbout"
android:label="@string/app_about"
android:exported="false"
android:label="@string/app_about" />
/>
<activity
android:name=".ActOSSLicense"
android:label="@string/oss_license"
android:exported="false"
android:label="@string/oss_license" />
/>
<activity
android:name=".ActMutedApp"
android:exported="false"
android:label="@string/muted_app" />
<activity
android:name=".ActMutedPseudoAccount"
android:exported="false"
android:label="@string/muted_users_from_pseudo_account" />
<activity
android:name=".ActMutedWord"
android:exported="false"
android:label="@string/muted_word" />
<activity
android:name=".ActFavMute"
android:exported="false"
android:label="@string/fav_muted_user_long" />
<activity
android:name=".ActKeywordFilter"
android:exported="false"
android:label="@string/keyword_filter_new"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActHighlightWordList"
android:exported="false"
android:label="@string/highlight_word" />
<activity
android:name=".ActHighlightWordEdit"
android:exported="false"
android:label="@string/highlight_word" />
<activity
android:name=".ActColumnCustomize"
android:exported="false"
android:label="@string/color_and_background"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActLanguageFilter"
android:exported="false"
android:label="@string/language_filter"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActDrawableList"
android:exported="false"
android:label="@string/drawable_list"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActNickname"
android:exported="false"
android:label="@string/nickname_and_color_and_notification_sound"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActText"
android:exported="false"
android:label="@string/select_and_copy"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".ActMediaViewer"
android:exported="false"
android:theme="@style/AppTheme.Dark.NoActionBar" />
<activity
android:name=".ActExitReasons"
android:exported="false"
android:label="@string/exit_reasons"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
@ -301,6 +300,18 @@
android:name="android.max_aspect"
android:value="100.0" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/Light_colorAccent" />
<meta-data
android:name="android.allow_multiple_resumed_activities"
android:value="true" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="jp.juggler.subwaytooter.FileProvider"
@ -311,38 +322,50 @@
android:resource="@xml/file_provider_path" />
</provider>
<receiver android:name=".DownloadReceiver">
<receiver
android:name=".EventReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.ACTION_MY_PACKAGE_REPLACED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver
android:name=".DownloadReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more. -->
<service
android:name=".notification.PollingService"
android:permission="android.permission.BIND_JOB_SERVICE" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more. -->
<service android:name=".notification.PollingForegrounder" />
<!--https://android-developers.googleblog.com/2018/11/get-your-app-ready-for-foldable-phones.html-->
<service
android:name=".MyFirebaseMessagingService"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/Light_colorAccent" />
<!--https://android-developers.googleblog.com/2018/11/get-your-app-ready-for-foldable-phones.html-->
<meta-data
android:name="android.allow_multiple_resumed_activities"
android:value="true" />
</application>
</manifest>

View File

@ -42,8 +42,6 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
internal const val COLOR_DIALOG_ID_ACCT_TEXT = 4
internal const val COLOR_DIALOG_ID_CONTENT_TEXT = 5
internal const val REQUEST_CODE_PICK_BACKGROUND = 1
internal const val PROGRESS_MAX = 65536
fun createIntent(activity: ActMain, idx: Int) =
@ -72,7 +70,7 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
private var lastImageUri: String? = null
private var lastImageBitmap: Bitmap? = null
val arColumnBackgroundImage = activityResultHandler { ar ->
private val arColumnBackgroundImage = activityResultHandler { ar ->
val data = ar?.data
if (data != null && ar.resultCode == RESULT_OK) {
data.handleGetContentResult(contentResolver)

View File

@ -14,8 +14,6 @@ import android.view.*
import android.view.inputmethod.EditorInfo
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import jp.juggler.subwaytooter.action.saveWindowSize
import jp.juggler.subwaytooter.actpost.*
import jp.juggler.subwaytooter.api.*
@ -56,11 +54,6 @@ class ActPost : AppCompatActivity(),
const val KEY_QUOTE = "quote"
const val KEY_SCHEDULED_STATUS = "scheduled_status"
const val KEY_ATTACHMENT_LIST = "attachment_list"
const val KEY_IN_REPLY_TO_ID = "in_reply_to_id"
const val KEY_IN_REPLY_TO_TEXT = "in_reply_to_text"
const val KEY_IN_REPLY_TO_IMAGE = "in_reply_to_image"
const val STATE_ALL = "all"
/////////////////////////////////////////////////
@ -97,8 +90,8 @@ class ActPost : AppCompatActivity(),
lateinit var btnAccount: Button
lateinit var btnVisibility: ImageButton
lateinit var btnAttachment: ImageButton
lateinit var btnPost: ImageButton
private lateinit var btnAttachment: ImageButton
private lateinit var btnPost: ImageButton
lateinit var llAttachment: View
lateinit var ivMedia: List<MyNetworkImageView>
lateinit var cbNSFW: CheckBox
@ -121,7 +114,7 @@ class ActPost : AppCompatActivity(),
lateinit var tvCharCount: TextView
lateinit var handler: Handler
lateinit var formRoot: ActPostRootLinearLayout
private lateinit var formRoot: ActPostRootLinearLayout
lateinit var llReply: View
lateinit var tvReplyTo: TextView
@ -129,8 +122,8 @@ class ActPost : AppCompatActivity(),
lateinit var scrollView: ScrollView
lateinit var tvSchedule: TextView
lateinit var ibSchedule: ImageButton
lateinit var ibScheduleReset: ImageButton
private lateinit var ibSchedule: ImageButton
private lateinit var ibScheduleReset: ImageButton
lateinit var pref: SharedPreferences
lateinit var appState: AppState
@ -184,41 +177,6 @@ class ActPost : AppCompatActivity(),
}
}
val commitContentListener =
InputConnectionCompat.OnCommitContentListener {
inputContentInfo: InputContentInfoCompat,
flags: Int,
_: Bundle?,
->
// Intercepts InputConnection#commitContent API calls.
// - inputContentInfo : content to be committed
// - flags : {@code 0} or {@link #INPUT_CONTENT_GRANT_READ_URI_PERMISSION}
// - opts : optional bundle data. This can be {@code null}
// return
// - true if this request is accepted by the application,
// no matter if the request is already handled or still being handled in background.
// - false to use the default implementation
// read and display inputContentInfo asynchronously
if (Build.VERSION.SDK_INT >= 25 &&
flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION != 0
) {
try {
inputContentInfo.requestPermission()
} catch (ignored: Exception) {
// return false if failed
return@OnCommitContentListener false
}
}
addAttachment(inputContentInfo.contentUri) {
inputContentInfo.releasePermission()
}
true
}
////////////////////////////////////////////////////////////////
override fun onCreate(savedInstanceState: Bundle?) {
@ -331,7 +289,11 @@ class ActPost : AppCompatActivity(),
openBrowser(span.linkInfo.url)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
attachmentPicker.onRequestPermissionsResult(requestCode, permissions, grantResults)
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
@ -407,7 +369,12 @@ class ActPost : AppCompatActivity(),
updateTextCount()
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
showPoll()
updateTextCount()
}
@ -463,7 +430,11 @@ class ActPost : AppCompatActivity(),
cbContentWarning.setOnCheckedChangeListener { _, _ -> showContentWarningEnabled() }
completionHelper = CompletionHelper(this, pref, appState.handler)
completionHelper.attachEditText(formRoot, etContent, false, object : CompletionHelper.Callback2 {
completionHelper.attachEditText(
formRoot,
etContent,
false,
object : CompletionHelper.Callback2 {
override fun onTextUpdate() {
updateTextCount()
}
@ -491,6 +462,6 @@ class ActPost : AppCompatActivity(),
scrollView.viewTreeObserver.addOnScrollChangedListener(scrollListener)
etContent.contentMineTypeArray = AttachmentUploader.acceptableMimeTypes.toTypedArray()
etContent.commitContentListener = commitContentListener
etContent.contentCallback = { addAttachment(it) }
}
}

View File

@ -98,7 +98,7 @@ class ActText : AppCompatActivity() {
return true
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.act_text, menu)
return super.onCreateOptionsMenu(menu)
}

View File

@ -348,15 +348,13 @@ class CompletionHelper(
}
})
et.setOnSelectionChangeListener(object : MyEditText.OnSelectionChangeListener {
override fun onSelectionChanged(selStart: Int, selEnd: Int) {
if (selStart != selEnd) {
// 範囲選択されてるならポップアップは閉じる
log.d("onSelectionChanged: range selected")
et.onSelectionChange = { selStart, selEnd ->
if (selStart != selEnd) {
log.d("onSelectionChange: range selected")
closeAcctPopup()
}
}
})
// 全然動いてなさそう…
// et.setCustomSelectionActionModeCallback( action_mode_callback );

View File

@ -105,7 +105,7 @@ open class TootApiResult(
}
// アカウント作成APIのdetailsを読むため、エラー応答のjsonオブジェクトを保持する
var errorJson: JsonObject? = null
private var errorJson: JsonObject? = null
internal fun simplifyErrorHtml(
sv: String,

View File

@ -379,7 +379,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
val result = Channel<Pair<TootInstance?, TootApiResult?>>()
}
fun queuedRequest(
private fun queuedRequest(
allowPixelfed: Boolean,
get: suspend (cached: TootInstance?) -> Pair<TootInstance?, TootApiResult?>,
) = QueuedRequest(allowPixelfed, get)

View File

@ -36,7 +36,7 @@ class TootReaction(
) {
companion object {
fun appendDomain(name: String, domain: String?) =
private fun appendDomain(name: String, domain: String?) =
if (domain?.isNotEmpty() == true) {
"$name@$domain"
} else {
@ -164,7 +164,7 @@ class TootReaction(
}
}
fun chooseUrl() = when {
private fun chooseUrl() = when {
PrefB.bpDisableEmojiAnimation(App1.pref) -> staticUrl
else -> url
}

View File

@ -15,7 +15,7 @@ class ColumnTask_Refresh(
private val bSilent: Boolean,
val bBottom: Boolean,
internal val postedStatusId: EntityId? = null,
internal val refreshAfterToot: Int = -1,
private val refreshAfterToot: Int = -1,
) : ColumnTask(
columnArg,
if (bBottom) ColumnTaskType.REFRESH_BOTTOM else ColumnTaskType.REFRESH_TOP

View File

@ -18,7 +18,7 @@ class UserRelationLoader(val column: Column) {
val whoSet = HashSet<EntityId>()
val acctSet = HashSet<String>()
val tagSet = HashSet<String>()
private val tagSet = HashSet<String>()
fun add(whoRef: TootAccountRef?) {
add(whoRef?.get())

View File

@ -80,7 +80,7 @@ class ColumnViewHolder(
lateinit var llColumnHeader: View
lateinit var tvColumnIndex: TextView
lateinit var tvColumnStatus: TextView
lateinit var tvColumnContext: TextView
private lateinit var tvColumnContext: TextView
lateinit var ivColumnIcon: ImageView
lateinit var tvColumnName: TextView
@ -143,7 +143,7 @@ class ColumnViewHolder(
lateinit var btnQuickFilterVote: ImageButton
lateinit var llRefreshError: FrameLayout
lateinit var ivRefreshError: ImageView
private lateinit var ivRefreshError: ImageView
lateinit var tvRefreshError: TextView
lateinit var llListList: View

View File

@ -552,7 +552,7 @@ internal class DlgContextMenu(
return true
}
fun onClickUpdateGroup(v: View): Boolean = when (v.id) {
private fun onClickUpdateGroup(v: View): Boolean = when (v.id) {
R.id.btnGroupStatusCrossAccount -> updateGroup(
btnGroupStatusCrossAccount,
llGroupStatusCrossAccount,

View File

@ -775,7 +775,7 @@ class ItemViewHolder(
}
}
fun _LinearLayout.inflateConversationIconOne() =
private fun _LinearLayout.inflateConversationIconOne() =
myNetworkImageView {
scaleType = ImageView.ScaleType.CENTER_CROP
}.lparams(dip(24), dip(24)) {

View File

@ -324,7 +324,7 @@ fun ItemViewHolder.onClickEnqueteChoice(
TootPollsType.Mastodon -> client.request(
"/api/v1/polls/${enquete.pollId}/votes",
jsonObject {
put("choices", jp.juggler.util.jsonArray { add(idx) })
put("choices", jsonArray { add(idx) })
}.toPostRequestBuilder()
)
TootPollsType.FriendsNico -> client.request(
@ -406,7 +406,7 @@ fun ItemViewHolder.sendMultiple(
client.request(
"/api/v1/polls/${enquete.pollId}/votes",
jsonObject {
put("choices", jp.juggler.util.jsonArray {
put("choices", jsonArray {
enquete.items.forEachIndexed { index, choice ->
if (choice.checked) add(index)
}

View File

@ -84,8 +84,8 @@ class StatusButtons(
private val colorAccent: Int
get() = activity.attrColor(R.attr.colorImageButtonAccent)
var optionalButtonFirst: View? = null
var optionalButtonCount = 0
private var optionalButtonFirst: View? = null
private var optionalButtonCount = 0
var ti: TootInstance? = null
init {

View File

@ -472,7 +472,7 @@ class PushSubscriptionHelper(
}
}
suspend fun canSkipSubscriptionMastodon(
private suspend fun canSkipSubscriptionMastodon(
client: TootApiClient,
clientIdentifier: String,
endpoint: String,

View File

@ -3,7 +3,7 @@ package jp.juggler.subwaytooter.span
import android.text.TextPaint
import android.text.style.CharacterStyle
class HighlightSpan(val colorFg: Int, val colorBg: Int) : CharacterStyle() {
class HighlightSpan(private val colorFg: Int, val colorBg: Int) : CharacterStyle() {
override fun updateDrawState(ds: TextPaint) {
if (colorFg != 0) ds.color = colorFg

View File

@ -31,7 +31,7 @@ class MyClickableSpan(val linkInfo: LinkInfo) : ClickableSpan() {
var showLinkUnderline = true
}
val colorFg: Int
private val colorFg: Int
val colorBg: Int
init {

View File

@ -223,7 +223,7 @@ class AttachmentPicker(
}
}
fun performCapture(action: String, errorCaption: String) {
private fun performCapture(action: String, errorCaption: String) {
try {
arCapture.launch(Intent(action))
} catch (ex: Throwable) {

View File

@ -69,10 +69,10 @@ class PostImpl(
private var visibilityChecked: TootVisibility? = null
var bConfirmTag: Boolean = false
private var bConfirmTag: Boolean = false
var bConfirmAccount: Boolean = false
var bConfirmRedraft: Boolean = false
var bConfirmTagCharacter: Boolean = false
private var bConfirmRedraft: Boolean = false
private var bConfirmTagCharacter: Boolean = false
private val choiceMaxChars = when {
account.isMisskey -> 15
@ -229,9 +229,9 @@ class PostImpl(
return true
}
var resultStatus: TootStatus? = null
var resultCredentialTmp: TootAccount? = null
var resultScheduledStatusSucceeded = false
private var resultStatus: TootStatus? = null
private var resultCredentialTmp: TootAccount? = null
private var resultScheduledStatusSucceeded = false
private suspend fun getCredential(
client: TootApiClient,

View File

@ -2,23 +2,53 @@ package jp.juggler.subwaytooter.view
import android.annotation.SuppressLint
import android.content.Context
import androidx.core.view.inputmethod.EditorInfoCompat
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.appcompat.widget.AppCompatEditText
import android.net.Uri
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import android.view.View
import androidx.appcompat.widget.AppCompatEditText
import androidx.core.view.ContentInfoCompat
import androidx.core.view.OnReceiveContentListener
import androidx.core.view.ViewCompat
import jp.juggler.util.LogCategory
class MyEditText : AppCompatEditText {
companion object {
private val log = LogCategory("MyEditText")
val MIME_TYPES = arrayOf("image/*")
}
private var mOnSelectionChangeListener: OnSelectionChangeListener? = null
// 選択範囲変更リスナ
var onSelectionChange: ((selStart: Int, selEnd: Int) -> Unit)? = null
// キーボードやDnDから画像を挿入するリスナ
var contentCallback: ((Uri) -> Unit)? = null
///////////////////////////////////////////////////////
// IMEから画像を送られてくることがあるらしい
var contentMineTypeArray: Array<String>? = null
private val receiveContentListener = object : OnReceiveContentListener {
override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat {
// 受け付けない状況では何も受け取らずに残りを返す
val contentCallback = contentCallback ?: return payload
val pair = payload.partition { item -> item.uri != null }
val uriContent = pair.first
val remaining = pair.second
if (uriContent != null) {
val clip = uriContent.clip
for (i in 0 until clip.itemCount) {
val uri = clip.getItemAt(i).uri
contentCallback(uri)
}
}
return remaining
}
}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
@ -28,20 +58,16 @@ class MyEditText : AppCompatEditText {
defStyleAttr
)
init {
ViewCompat.setOnReceiveContentListener(this, MIME_TYPES, receiveContentListener)
}
////////////////////////////////////////////////////
// 選択範囲変更イベントをコールバックに渡す
interface OnSelectionChangeListener {
fun onSelectionChanged(selStart: Int, selEnd: Int)
}
fun setOnSelectionChangeListener(listener: OnSelectionChangeListener) {
mOnSelectionChangeListener = listener
}
// 選択範囲変更の傍受
override fun onSelectionChanged(selStart: Int, selEnd: Int) {
super.onSelectionChanged(selStart, selEnd)
mOnSelectionChangeListener?.onSelectionChanged(selStart, selEnd)
onSelectionChange?.invoke(selStart, selEnd)
}
////////////////////////////////////////////////////
@ -61,26 +87,4 @@ class MyEditText : AppCompatEditText {
// at android.view.View.dispatchTouchEvent (View.java:9303)
}
}
///////////////////////////////////////////////////////
// IMEから画像を送られてくることがあるらしい
var commitContentListener: InputConnectionCompat.OnCommitContentListener? = null
var contentMineTypeArray: Array<String>? = null
override fun onCreateInputConnection(outAttrs: EditorInfo?): InputConnection? {
log.d("onCreateInputConnection: listener=$commitContentListener")
val superIc = super.onCreateInputConnection(outAttrs)
val listener = commitContentListener
val mimeArray = contentMineTypeArray
return if (listener == null || mimeArray == null || outAttrs == null) {
superIc
} else {
EditorInfoCompat.setContentMimeTypes(outAttrs, mimeArray)
superIc?.let { InputConnectionCompat.createWrapper(it, outAttrs, listener) }
}
}
}

View File

@ -3,6 +3,7 @@ package jp.juggler.util
import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import androidx.annotation.IntRange
/////////////////////////////////////////////////////////////
// SQLite にBooleanをそのまま保存することはできないのでInt型との変換が必要になる
@ -19,16 +20,21 @@ fun Int.i2b() = this != 0
// getBoolean(getColumnIndex(key))
fun Cursor.getInt(key: String) =
getInt(getColumnIndex(key))
getColumnIndex(key).takeIf { it >= 0 }?.let { getInt(it) }
?: error("getInt: missing column named $key")
fun Cursor.getIntOrNull(idx: Int) =
if (isNull(idx)) null else getInt(idx)
fun Cursor.getIntOrNull(@IntRange(from = 0) idx: Int) = when {
idx < 0 -> error("getIntOrNull: invalid index $idx")
isNull(idx) -> null
else -> getInt(idx)
}
fun Cursor.getIntOrNull(key: String) =
getIntOrNull(getColumnIndex(key))
getColumnIndex(key).takeIf { it >= 0 }?.let { getIntOrNull(it) }
fun Cursor.getLong(key: String) =
getLong(getColumnIndex(key))
getColumnIndex(key).takeIf { it >= 0 }?.let { getLong(it) }
?: error("getLong: missing column named $key")
//fun Cursor.getLongOrNull(idx:Int) =
// if(isNull(idx)) null else getLong(idx)
@ -37,7 +43,8 @@ fun Cursor.getLong(key: String) =
// getLongOrNull(getColumnIndex(key))
fun Cursor.getString(key: String): String =
getString(getColumnIndex(key))
getColumnIndex(key).takeIf { it >= 0 }?.let { getString(it)!! }
?: error("getString: missing column named $key")
fun Cursor.getStringOrNull(keyIdx: Int) =
if (isNull(keyIdx)) null else getString(keyIdx)
@ -69,7 +76,7 @@ class ColumnMeta(
class List(
val table: String,
val initialVersion: Int,
private val initialVersion: Int,
var createExtra: () -> Array<String> = { emptyArray() },
var deleteBeforeCreate: Boolean = false,
) : ArrayList<ColumnMeta>() {
@ -143,15 +150,9 @@ fun ContentValues.put(key: ColumnMeta, v: Float?) = put(key.name, v)
fun ContentValues.put(key: ColumnMeta, v: Double?) = put(key.name, v)
fun ContentValues.put(key: ColumnMeta, v: ByteArray?) = put(key.name, v)
fun Cursor.getInt(key: ColumnMeta) = getInt(getColumnIndex(key.name))
fun Cursor.getBoolean(key: ColumnMeta) = getInt(key).i2b()
fun Cursor.getLong(key: ColumnMeta) = getLong(getColumnIndex(key.name))
@Suppress("unused")
fun Cursor.getIntOrNull(key: ColumnMeta) = getIntOrNull(getColumnIndex(key.name))
fun Cursor.getString(key: ColumnMeta): String = getString(getColumnIndex(key.name))
fun Cursor.getStringOrNull(key: ColumnMeta): String? {
val idx = key.getIndex(this)
return if (isNull(idx)) null else getString(idx)
}
fun Cursor.getInt(key: ColumnMeta) = getInt(key.name)
fun Cursor.getBoolean(key: ColumnMeta) = getInt(key.name).i2b()
fun Cursor.getLong(key: ColumnMeta) = getLong(key.name)
fun Cursor.getIntOrNull(key: ColumnMeta) = getIntOrNull(key.name)
fun Cursor.getString(key: ColumnMeta): String = getString(key.name)
fun Cursor.getStringOrNull(key: ColumnMeta): String? = getStringOrNull(key.name)

View File

@ -23,7 +23,7 @@ class LogCategory(category: String) {
///////////////////////////////
// string
fun msg(priority: Int, msg: String): Boolean {
private fun msg(priority: Int, msg: String): Boolean {
Log.println(priority, tag, msg)
return false
}
@ -37,7 +37,7 @@ class LogCategory(category: String) {
///////////////////////////////
// Resources.getString()
fun msg(priority: Int, res: Resources, @StringRes stringId: Int, args: Array<out Any?>) =
private fun msg(priority: Int, res: Resources, @StringRes stringId: Int, args: Array<out Any?>) =
msg(priority, res.getString(stringId, *args))
fun e(res: Resources, @StringRes stringId: Int, vararg args: Any) =
@ -58,7 +58,7 @@ class LogCategory(category: String) {
///////////////////////////////
// Throwable + string
fun msg(priority: Int, ex: Throwable, caption: String = "exception.") =
private fun msg(priority: Int, ex: Throwable, caption: String = "exception.") =
msg(priority, ex.withCaption(caption))
fun e(ex: Throwable, caption: String = "exception") = msg(Log.ERROR, ex, caption)

View File

@ -1,19 +1,22 @@
buildscript {
ext.min_sdk_version = 21
ext.target_sdk_version = 30
ext.compile_sdk_version = 30
ext.target_sdk_version = 31
ext.compile_sdk_version = 31
ext.appcompat_version='1.3.0'
ext.lifecycle_version='2.3.1'
ext.appcompat_version='1.3.1'
ext.lifecycle_version="2.4.0-rc01"
ext.arch_version = "2.1.0"
ext.kotlin_version = '1.5.20'
ext.kotlinx_coroutines_version = '1.5.0'
ext.kotlin_version = '1.5.31'
ext.kotlinx_coroutines_version = '1.5.2'
ext.anko_version='0.10.8'
ext.junit_version='4.13.2'
ext.detekt_version='1.18.0'
ext.detekt_version='1.18.1'
ext.compose_version = '1.0.4'
repositories {
google()
@ -21,7 +24,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
classpath 'com.android.tools.build:gradle:7.0.3'
classpath 'com.google.gms:google-services:4.3.10'
//noinspection DifferentKotlinGradleVersion

View File

@ -33,11 +33,11 @@ import androidx.annotation.NonNull;
*/
class AlphaPatternDrawable extends Drawable {
private int rectangleSize;
private final int rectangleSize;
private Paint paint = new Paint();
private Paint paintWhite = new Paint();
private Paint paintGray = new Paint();
private final Paint paint = new Paint();
private final Paint paintWhite = new Paint();
private final Paint paintGray = new Paint();
private int numRectanglesHorizontal;
private int numRectanglesVertical;
@ -55,25 +55,30 @@ class AlphaPatternDrawable extends Drawable {
paintGray.setColor(0xFFCBCBCB);
}
@Override public void draw( @NonNull Canvas canvas ){
@Override
public void draw(@NonNull Canvas canvas) {
if (bitmap != null && !bitmap.isRecycled()) {
canvas.drawBitmap(bitmap, null, getBounds(), paint);
}
}
@Override public int getOpacity(){
@Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
@Override public void setAlpha( int alpha ){
@Override
public void setAlpha(int alpha) {
throw new UnsupportedOperationException("Alpha is not supported by this drawable.");
}
@Override public void setColorFilter( ColorFilter cf ){
@Override
public void setColorFilter(ColorFilter cf) {
throw new UnsupportedOperationException("ColorFilter is not supported by this drawable.");
}
@Override protected void onBoundsChange( Rect bounds ){
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
int height = bounds.height();
int width = bounds.width();

View File

@ -19,18 +19,19 @@ package com.jrummyapps.android.colorpicker;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import androidx.core.graphics.ColorUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import androidx.core.graphics.ColorUtils;
class ColorPaletteAdapter extends BaseAdapter {
/*package*/ final OnColorSelectedListener listener;
/*package*/ final int[] colors;
/*package*/ int selectedPosition;
/*package*/ int colorShape;
/*package*/ final int colorShape;
ColorPaletteAdapter(OnColorSelectedListener listener,
int[] colors,
@ -42,19 +43,23 @@ class ColorPaletteAdapter extends BaseAdapter {
this.colorShape = colorShape;
}
@Override public int getCount() {
@Override
public int getCount() {
return colors.length;
}
@Override public Object getItem(int position) {
@Override
public Object getItem(int position) {
return colors[position];
}
@Override public long getItemId(int position) {
@Override
public long getItemId(int position) {
return position;
}
@Override public View getView(int position, View convertView, ViewGroup parent) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder(parent.getContext());
@ -78,10 +83,10 @@ class ColorPaletteAdapter extends BaseAdapter {
private final class ViewHolder {
View view;
ColorPanelView colorPanelView;
ImageView imageView;
int originalBorderColor;
final View view;
final ColorPanelView colorPanelView;
final ImageView imageView;
final int originalBorderColor;
ViewHolder(Context context) {
int layoutResId;
@ -91,8 +96,8 @@ class ColorPaletteAdapter extends BaseAdapter {
layoutResId = R.layout.cpv_color_item_circle;
}
view = View.inflate(context, layoutResId, null);
colorPanelView = (ColorPanelView) view.findViewById(R.id.cpv_color_panel_view);
imageView = (ImageView) view.findViewById(R.id.cpv_color_image_view);
colorPanelView = view.findViewById(R.id.cpv_color_panel_view);
imageView = view.findViewById(R.id.cpv_color_image_view);
originalBorderColor = colorPanelView.getBorderColor();
view.setTag(this);
}
@ -117,20 +122,16 @@ class ColorPaletteAdapter extends BaseAdapter {
}
private void setOnClickListener(final int position) {
colorPanelView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
colorPanelView.setOnClickListener(v -> {
if (selectedPosition != position) {
selectedPosition = position;
notifyDataSetChanged();
}
listener.onColorSelected(colors[position]);
}
});
colorPanelView.setOnLongClickListener(new View.OnLongClickListener() {
@Override public boolean onLongClick(View v) {
colorPanelView.setOnLongClickListener(v -> {
colorPanelView.showHint();
return true;
}
});
}

View File

@ -141,22 +141,22 @@ public class ColorPanelView extends View {
} else if (shape == ColorShape.CIRCLE) {
final int outerRadius = getMeasuredWidth() / 2;
if (borderWidthPx > 0) {
canvas.drawCircle(getMeasuredWidth() / 2,
getMeasuredHeight() / 2,
canvas.drawCircle(getMeasuredWidth() / 2f,
getMeasuredHeight() / 2f,
outerRadius,
borderPaint);
}
if (Color.alpha(color) < 255) {
canvas.drawCircle(getMeasuredWidth() / 2,
getMeasuredHeight() / 2,
canvas.drawCircle(getMeasuredWidth() / 2f,
getMeasuredHeight() / 2f,
outerRadius - borderWidthPx, alphaPaint);
}
if (showOldColor) {
canvas.drawArc(centerRect, 90, 180, true, originalPaint);
canvas.drawArc(centerRect, 270, 180, true, colorPaint);
} else {
canvas.drawCircle(getMeasuredWidth() / 2,
getMeasuredHeight() / 2,
canvas.drawCircle(getMeasuredWidth() / 2f,
getMeasuredHeight() / 2f,
outerRadius - borderWidthPx,
colorPaint);
}
@ -169,6 +169,7 @@ public class ColorPanelView extends View {
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
} else if (shape == ColorShape.CIRCLE) {
//noinspection SuspiciousNameCombination
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
} else {

View File

@ -19,19 +19,12 @@ package com.jrummyapps.android.colorpicker;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Bundle;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.graphics.ColorUtils;
import androidx.appcompat.app.AlertDialog;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
@ -50,6 +43,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.jrummyapps.android.colorpicker.ColorPickerView.OnColorChangedListener;
import java.lang.annotation.Retention;
@ -57,6 +51,15 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Locale;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.core.graphics.ColorUtils;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
/**
* <p>A dialog to pick a color.</p>
*
@ -68,7 +71,9 @@ import java.util.Locale;
* ColorPickerDialog.newBuilder().show(activity);
* </pre>
*/
public class ColorPickerDialog extends DialogFragment implements OnTouchListener, OnColorChangedListener, TextWatcher {
public class ColorPickerDialog
extends DialogFragment
implements OnTouchListener, OnColorChangedListener, TextWatcher {
private static final String ARG_ID = "id";
private static final String ARG_TYPE = "dialogType";
@ -123,7 +128,8 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
ColorPickerDialogListener colorPickerDialogListener;
FrameLayout rootView;
int[] presets;
@ColorInt int color;
@ColorInt
int color;
int dialogType;
int dialogId;
boolean showColorShades;
@ -142,50 +148,58 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
boolean showAlphaSlider;
private boolean fromEditText;
@Override public void onAttach(Activity activity) {
super.onAttach(activity);
if (colorPickerDialogListener == null && activity instanceof ColorPickerDialogListener) {
colorPickerDialogListener = (ColorPickerDialogListener) activity;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (colorPickerDialogListener == null && context instanceof ColorPickerDialogListener) {
colorPickerDialogListener = (ColorPickerDialogListener) context;
}
}
@Override public Dialog onCreateDialog(Bundle savedInstanceState) {
dialogId = getArguments().getInt(ARG_ID);
showAlphaSlider = getArguments().getBoolean(ARG_ALPHA);
showColorShades = getArguments().getBoolean(ARG_SHOW_COLOR_SHADES);
colorShape = getArguments().getInt(ARG_COLOR_SHAPE);
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle args = getArguments();
if (args == null) throw new RuntimeException("onCreateDialog: args is null");
Context context = getContext();
if (context == null) throw new RuntimeException("onCreateDialog: context is null");
Activity activity = getActivity();
if (activity == null) throw new RuntimeException("onCreateDialog: activity is null");
dialogId = args.getInt(ARG_ID);
showAlphaSlider = args.getBoolean(ARG_ALPHA);
showColorShades = args.getBoolean(ARG_SHOW_COLOR_SHADES);
colorShape = args.getInt(ARG_COLOR_SHAPE);
if (savedInstanceState == null) {
color = getArguments().getInt(ARG_COLOR);
dialogType = getArguments().getInt(ARG_TYPE);
color = args.getInt(ARG_COLOR);
dialogType = args.getInt(ARG_TYPE);
} else {
color = savedInstanceState.getInt(ARG_COLOR);
dialogType = savedInstanceState.getInt(ARG_TYPE);
}
rootView = new FrameLayout(getActivity());
rootView = new FrameLayout(activity);
if (dialogType == TYPE_CUSTOM) {
rootView.addView(createPickerView());
} else if (dialogType == TYPE_PRESETS) {
rootView.addView(createPresetsView());
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setView(rootView)
.setPositiveButton(R.string.cpv_select, new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
colorPickerDialogListener.onColorSelected(dialogId, color);
}
});
.setPositiveButton(R.string.cpv_select, (dialog, which) ->
colorPickerDialogListener.onColorSelected(dialogId, color));
int dialogTitleStringRes = getArguments().getInt(ARG_DIALOG_TITLE);
int dialogTitleStringRes = args.getInt(ARG_DIALOG_TITLE);
if (dialogTitleStringRes != 0) {
builder.setTitle(dialogTitleStringRes);
}
int neutralButtonStringRes;
if (dialogType == TYPE_CUSTOM && getArguments().getBoolean(ARG_ALLOW_PRESETS)) {
if (dialogType == TYPE_CUSTOM && args.getBoolean(ARG_ALLOW_PRESETS)) {
neutralButtonStringRes = R.string.cpv_presets;
} else if (dialogType == TYPE_PRESETS && getArguments().getBoolean(ARG_ALLOW_CUSTOM)) {
} else if (dialogType == TYPE_PRESETS && args.getBoolean(ARG_ALLOW_CUSTOM)) {
neutralButtonStringRes = R.string.cpv_custom;
} else {
neutralButtonStringRes = 0;
@ -198,21 +212,22 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
return builder.create();
}
@Override public void onStart() {
@Override
public void onStart() {
super.onStart();
AlertDialog dialog = (AlertDialog) getDialog();
// http://stackoverflow.com/a/16972670/1048340
//noinspection ConstantConditions
dialog.getWindow()
.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
// Do not dismiss the dialog when clicking the neutral button.
Button neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
if (neutralButton != null) {
neutralButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
neutralButton.setOnClickListener(v -> {
rootView.removeAllViews();
switch (dialogType) {
case TYPE_CUSTOM:
@ -225,17 +240,18 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
((Button) v).setText(R.string.cpv_presets);
rootView.addView(createPickerView());
}
}
});
}
}
@Override public void onDismiss(DialogInterface dialog) {
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog);
colorPickerDialogListener.onDialogDismissed(dialogId);
}
@Override public void onSaveInstanceState(Bundle outState) {
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(ARG_COLOR, color);
outState.putInt(ARG_TYPE, dialogType);
super.onSaveInstanceState(outState);
@ -244,8 +260,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Set the callback
*
* @param colorPickerDialogListener
* The callback invoked when a color is selected or the dialog is dismissed.
* @param colorPickerDialogListener The callback invoked when a color is selected or the dialog is dismissed.
*/
public void setColorPickerDialogListener(ColorPickerDialogListener colorPickerDialogListener) {
this.colorPickerDialogListener = colorPickerDialogListener;
@ -254,6 +269,11 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
// region Custom Picker
View createPickerView() {
Bundle args = getArguments();
if (args == null) throw new RuntimeException("createPickerView: args is null");
FragmentActivity activity = getActivity();
if (activity == null) throw new RuntimeException("createPickerView: activity is null");
View contentView = View.inflate(getActivity(), R.layout.cpv_dialog_color_picker, null);
colorPicker = contentView.findViewById(R.id.cpv_color_picker_view);
ColorPanelView oldColorPanel = contentView.findViewById(R.id.cpv_color_panel_old);
@ -263,8 +283,10 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
try {
final TypedValue value = new TypedValue();
TypedArray typedArray =
getActivity().obtainStyledAttributes(value.data, new int[]{android.R.attr.textColorPrimary});
TypedArray typedArray = activity.obtainStyledAttributes(
value.data,
new int[]{android.R.attr.textColorPrimary}
);
int arrowColor = typedArray.getColor(0, Color.BLACK);
typedArray.recycle();
arrowRight.setColorFilter(arrowColor);
@ -272,7 +294,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
}
colorPicker.setAlphaSliderVisible(showAlphaSlider);
oldColorPanel.setColor(getArguments().getInt(ARG_COLOR));
oldColorPanel.setColor(args.getInt(ARG_COLOR));
colorPicker.setColor(color, true);
newColorPanel.setColor(color);
setHex(color);
@ -281,37 +303,35 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
hexEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(6)});
}
newColorPanel.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
newColorPanel.setOnClickListener(v -> {
if (newColorPanel.getColor() == color) {
colorPickerDialogListener.onColorSelected(dialogId, color);
dismiss();
}
}
});
contentView.setOnTouchListener(this);
colorPicker.setOnColorChangedListener(this);
hexEditText.addTextChangedListener(this);
hexEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
hexEditText.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
InputMethodManager imm = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) imm.showSoftInput(hexEditText, InputMethodManager.SHOW_IMPLICIT);
}
}
});
return contentView;
}
@SuppressLint("ClickableViewAccessibility")
@Override public boolean onTouch( View v, MotionEvent event) {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v != hexEditText && hexEditText.hasFocus()) {
hexEditText.clearFocus();
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
InputMethodManager imm = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) imm.hideSoftInputFromWindow(hexEditText.getWindowToken(), 0);
hexEditText.clearFocus();
return true;
@ -319,13 +339,15 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
return false;
}
@Override public void onColorChanged(int newColor) {
@Override
public void onColorChanged(int newColor) {
color = newColor;
newColorPanel.setColor(newColor);
if (!fromEditText) {
setHex(newColor);
if (hexEditText.hasFocus()) {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
InputMethodManager imm = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) imm.hideSoftInputFromWindow(hexEditText.getWindowToken(), 0);
hexEditText.clearFocus();
}
@ -333,15 +355,18 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
fromEditText = false;
}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override public void afterTextChanged(Editable s) {
@Override
public void afterTextChanged(Editable s) {
if (hexEditText.isFocused()) {
try {
int color = parseColorString(s.toString());
@ -437,8 +462,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
contentView.findViewById(R.id.shades_divider).setVisibility(View.GONE);
}
adapter = new ColorPaletteAdapter(new ColorPaletteAdapter.OnColorSelectedListener() {
@Override public void onColorSelected(int newColor) {
adapter = new ColorPaletteAdapter(newColor -> {
if (color == newColor) {
colorPickerDialogListener.onColorSelected(dialogId, color);
dismiss();
@ -448,7 +472,6 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
if (showColorShades) {
createColorShades(color);
}
}
}, presets, getSelectedItemPosition(), colorShape);
gridView.setAdapter(adapter);
@ -468,7 +491,8 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
presets = getArguments().getIntArray(ARG_PRESETS);
if (presets == null) presets = MATERIAL_COLORS;
boolean isMaterialColors = presets == MATERIAL_COLORS;
presets = Arrays.copyOf(presets, presets.length); // don't update the original array when modifying alpha
presets = Arrays.copyOf(presets, presets.length);
// don't update the original array when modifying alpha
if (alpha != 255) {
// add alpha to the presets
for (int i = 0; i < presets.length; i++) {
@ -501,7 +525,8 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
return;
}
final int horizontalPadding = getResources().getDimensionPixelSize(R.dimen.cpv_item_horizontal_padding);
final int horizontalPadding = getResources()
.getDimensionPixelSize(R.dimen.cpv_item_horizontal_padding);
for (final int colorShade : colorShades) {
int layoutResId;
@ -514,21 +539,19 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
final View view = View.inflate(getActivity(), layoutResId, null);
final ColorPanelView colorPanelView = view.findViewById(R.id.cpv_color_panel_view);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) colorPanelView.getLayoutParams();
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) colorPanelView
.getLayoutParams();
params.leftMargin = params.rightMargin = horizontalPadding;
colorPanelView.setLayoutParams(params);
colorPanelView.setColor(colorShade);
shadesLayout.addView(view);
colorPanelView.post(new Runnable() {
@Override public void run() {
colorPanelView.post(() -> {
// The color is black when rotating the dialog. This is a dirty fix. WTF!?
colorPanelView.setColor(colorShade);
}
});
colorPanelView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
colorPanelView.setOnClickListener(v -> {
if (v.getTag() instanceof Boolean && (Boolean) v.getTag()) {
colorPickerDialogListener.onColorSelected(dialogId, ColorPickerDialog.this.color);
dismiss();
@ -549,13 +572,10 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
}
cpv.setTag(cpv == v);
}
}
});
colorPanelView.setOnLongClickListener(new View.OnLongClickListener() {
@Override public boolean onLongClick(View v) {
colorPanelView.setOnLongClickListener(v -> {
colorPanelView.showHint();
return true;
}
});
}
}
@ -599,7 +619,8 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
int percentage = (int) ((double) progress * 100 / 255);
transparencyPercText.setText(String.format(Locale.ENGLISH, "%d%%", percentage));
transparencySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int percentage = (int) ((double) progress * 100 / 255);
transparencyPercText.setText(String.format(Locale.ENGLISH, "%d%%", percentage));
int alpha = 255 - progress;
@ -649,11 +670,13 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
color = Color.argb(alpha, red, green, blue);
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
@ -667,14 +690,14 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
break;
}
}
if (!present) {
if (present) {
return array;
}
int[] newArray = new int[array.length + 1];
newArray[0] = value;
System.arraycopy(array, 0, newArray, 1, newArray.length - 1);
return newArray;
}
return array;
}
private int[] pushIfNotExists(int[] array, int value) {
boolean present = false;
@ -706,18 +729,23 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
// region Builder
@SuppressWarnings("WeakerAccess") public static final class Builder {
@SuppressWarnings("WeakerAccess")
public static final class Builder {
@StringRes int dialogTitle = R.string.cpv_default_title;
@DialogType int dialogType = TYPE_PRESETS;
@StringRes
int dialogTitle = R.string.cpv_default_title;
@DialogType
int dialogType = TYPE_PRESETS;
int[] presets = MATERIAL_COLORS;
@ColorInt int color = Color.BLACK;
@ColorInt
int color = Color.BLACK;
int dialogId = 0;
boolean showAlphaSlider = false;
boolean allowPresets = true;
boolean allowCustom = true;
boolean showColorShades = true;
@ColorShape int colorShape = ColorShape.CIRCLE;
@ColorShape
int colorShape = ColorShape.CIRCLE;
/*package*/ Builder() {
@ -726,8 +754,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Set the dialog title string resource id
*
* @param dialogTitle
* The string resource used for the dialog title
* @param dialogTitle The string resource used for the dialog title
* @return This builder object for chaining method calls
*/
public Builder setDialogTitle(@StringRes int dialogTitle) {
@ -738,8 +765,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Set which dialog view to show.
*
* @param dialogType
* Either {@link ColorPickerDialog#TYPE_CUSTOM} or {@link ColorPickerDialog#TYPE_PRESETS}.
* @param dialogType Either {@link ColorPickerDialog#TYPE_CUSTOM} or {@link ColorPickerDialog#TYPE_PRESETS}.
* @return This builder object for chaining method calls
*/
public Builder setDialogType(@DialogType int dialogType) {
@ -750,8 +776,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Set the colors used for the presets
*
* @param presets
* An array of color ints.
* @param presets An array of color ints.
* @return This builder object for chaining method calls
*/
public Builder setPresets(@NonNull int[] presets) {
@ -762,8 +787,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Set the original color
*
* @param color
* The default color for the color picker
* @param color The default color for the color picker
* @return This builder object for chaining method calls
*/
public Builder setColor(int color) {
@ -774,8 +798,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Set the dialog id used for callbacks
*
* @param dialogId
* The id that is sent back to the {@link ColorPickerDialogListener}.
* @param dialogId The id that is sent back to the {@link ColorPickerDialogListener}.
* @return This builder object for chaining method calls
*/
public Builder setDialogId(int dialogId) {
@ -786,8 +809,8 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Show the alpha slider
*
* @param showAlphaSlider
* {@code true} to show the alpha slider. Currently only supported with the {@link ColorPickerView}.
* @param showAlphaSlider {@code true} to show the alpha slider. Currently only supported with
* the {@link ColorPickerView}.
* @return This builder object for chaining method calls
*/
public Builder setShowAlphaSlider(boolean showAlphaSlider) {
@ -798,8 +821,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Show/Hide a neutral button to select preset colors.
*
* @param allowPresets
* {@code false} to disable showing the presets button.
* @param allowPresets {@code false} to disable showing the presets button.
* @return This builder object for chaining method calls
*/
public Builder setAllowPresets(boolean allowPresets) {
@ -810,8 +832,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Show/Hide the neutral button to select a custom color.
*
* @param allowCustom
* {@code false} to disable showing the custom button.
* @param allowCustom {@code false} to disable showing the custom button.
* @return This builder object for chaining method calls
*/
public Builder setAllowCustom(boolean allowCustom) {
@ -822,8 +843,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Show/Hide the color shades in the presets picker
*
* @param showColorShades
* {@code false} to hide the color shades.
* @param showColorShades {@code false} to hide the color shades.
* @return This builder object for chaining method calls
*/
public Builder setShowColorShades(boolean showColorShades) {
@ -834,8 +854,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Set the shape of the color panel view.
*
* @param colorShape
* Either {@link ColorShape#CIRCLE} or {@link ColorShape#SQUARE}.
* @param colorShape Either {@link ColorShape#CIRCLE} or {@link ColorShape#SQUARE}.
* @return This builder object for chaining method calls
*/
public Builder setColorShape(int colorShape) {
@ -847,7 +866,7 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
* Create the {@link ColorPickerDialog} instance.
*
* @return A new {@link ColorPickerDialog}.
* @see #show(Activity)
* @see #show(FragmentActivity)
*/
public ColorPickerDialog create() {
ColorPickerDialog dialog = new ColorPickerDialog();
@ -869,13 +888,11 @@ public class ColorPickerDialog extends DialogFragment implements OnTouchListener
/**
* Create and show the {@link ColorPickerDialog} created with this builder.
*
* @param activity
* The current activity.
* @param activity The current activity.
*/
public void show(Activity activity) {
create().show(activity.getFragmentManager(), "color-picker-dialog");
public void show(FragmentActivity activity) {
create().show(activity.getSupportFragmentManager(), "color-picker-dialog");
}
}
@Retention(RetentionPolicy.SOURCE)

View File

@ -159,7 +159,8 @@ public class ColorPickerView extends View {
init(context, attrs);
}
@Override public Parcelable onSaveInstanceState(){
@Override
public Parcelable onSaveInstanceState() {
Bundle state = new Bundle();
state.putParcelable("instanceState", super.onSaveInstanceState());
state.putInt("alpha", alpha);
@ -172,7 +173,8 @@ public class ColorPickerView extends View {
return state;
}
@Override public void onRestoreInstanceState( Parcelable state ){
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
@ -262,7 +264,8 @@ public class ColorPickerView extends View {
}
@Override protected void onDraw( Canvas canvas ){
@Override
protected void onDraw(Canvas canvas) {
if (drawingRect.width() <= 0 || drawingRect.height() <= 0) {
return;
}
@ -552,7 +555,8 @@ public class ColorPickerView extends View {
}
@SuppressLint("ClickableViewAccessibility")
@Override public boolean onTouchEvent( MotionEvent event ){
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
this.getParent().requestDisallowInterceptTouchEvent(true);
@ -617,7 +621,8 @@ public class ColorPickerView extends View {
return update;
}
@Override protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ){
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int finalWidth;
int finalHeight;
@ -639,12 +644,8 @@ public class ColorPickerView extends View {
h += panelSpacingPx + alphaPanelHeightPx;
}
if( h > heightAllowed ){
//We can't fit the view in this container, set the size to whatever was allowed.
finalHeight = heightAllowed;
}else{
finalHeight = h;
}
finalHeight = Math.min(h, heightAllowed);
finalWidth = widthAllowed;
@ -657,12 +658,8 @@ public class ColorPickerView extends View {
w -= (panelSpacingPx + alphaPanelHeightPx);
}
if( w > widthAllowed ){
//we can't fit within this container, set the size to whatever was allowed.
finalWidth = widthAllowed;
}else{
finalWidth = w;
}
finalWidth = Math.min(w, widthAllowed);
finalHeight = heightAllowed;
@ -722,39 +719,28 @@ public class ColorPickerView extends View {
finalHeight + getPaddingTop() + getPaddingBottom());
}
// private int getPreferredWidth() {
// //Our preferred width and height is 200dp for the square sat / val rectangle.
// int width = DrawingUtils.dpToPx(getContext(), 200);
//
// return (width + huePanelWidthPx + panelSpacingPx);
// }
//
// private int getPreferredHeight() {
// int height = DrawingUtils.dpToPx(getContext(), 200);
//
// if (showAlphaPanel) {
// height += panelSpacingPx + alphaPanelHeightPx;
// }
// return height;
// }
@Override public int getPaddingTop(){
@Override
public int getPaddingTop() {
return Math.max(super.getPaddingTop(), mRequiredPadding);
}
@Override public int getPaddingBottom(){
@Override
public int getPaddingBottom() {
return Math.max(super.getPaddingBottom(), mRequiredPadding);
}
@Override public int getPaddingLeft(){
@Override
public int getPaddingLeft() {
return Math.max(super.getPaddingLeft(), mRequiredPadding);
}
@Override public int getPaddingRight(){
@Override
public int getPaddingRight() {
return Math.max(super.getPaddingRight(), mRequiredPadding);
}
@Override protected void onSizeChanged( int w, int h, int oldw, int oldh ){
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
drawingRect = new Rect();
@ -983,15 +969,13 @@ public class ColorPickerView extends View {
return alphaSliderText;
}
private class BitmapCache {
private static class BitmapCache {
public Canvas canvas;
public Bitmap bitmap;
public float value;
}
public interface OnColorChangedListener {
void onColorChanged(int newColor);
}

View File

@ -16,17 +16,21 @@
package com.jrummyapps.android.colorpicker;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.preference.Preference;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
import com.jrummyapps.android.colorpicker.ColorPickerDialog.DialogType;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
/**
* A Preference to select a color
*/
@ -89,7 +93,8 @@ public class ColorPreference extends Preference implements ColorPickerDialogList
a.recycle();
}
@Override protected void onClick() {
@Override
protected void onClick() {
super.onClick();
if (onShowDialogListener != null) {
onShowDialogListener.onShowColorPickerDialog((String) getTitle(), color);
@ -106,18 +111,28 @@ public class ColorPreference extends Preference implements ColorPickerDialogList
.setColor(color)
.create();
dialog.setColorPickerDialogListener(ColorPreference.this);
Activity activity = (Activity) getContext();
dialog.show(activity.getFragmentManager(), getFragmentTag());
FragmentManager fm = getFragmentManager();
if (fm != null) {
dialog.show(fm, getFragmentTag());
}
}
}
@Override protected void onAttachedToActivity() {
@Nullable
private FragmentManager getFragmentManager() {
Context context = getContext();
if (context instanceof FragmentActivity) {
return ((FragmentActivity) context).getSupportFragmentManager();
}
return null;
}
@Override
protected void onAttachedToActivity() {
super.onAttachedToActivity();
if (showDialog) {
Activity activity = (Activity) getContext();
ColorPickerDialog fragment =
(ColorPickerDialog) activity.getFragmentManager().findFragmentByTag(getFragmentTag());
FragmentManager fm = getFragmentManager();
if (showDialog && fm != null) {
ColorPickerDialog fragment = (ColorPickerDialog) fm.findFragmentByTag(getFragmentTag());
if (fragment != null) {
// re-bind preference to fragment
fragment.setColorPickerDialogListener(this);
@ -125,15 +140,17 @@ public class ColorPreference extends Preference implements ColorPickerDialogList
}
}
@Override protected void onBindView(View view) {
@Override
protected void onBindView(View view) {
super.onBindView(view);
ColorPanelView preview = (ColorPanelView) view.findViewById(R.id.cpv_preference_preview_color_panel);
ColorPanelView preview = view.findViewById(R.id.cpv_preference_preview_color_panel);
if (preview != null) {
preview.setColor(color);
}
}
@Override protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
color = getPersistedInt(0xFF000000);
} else {
@ -142,23 +159,25 @@ public class ColorPreference extends Preference implements ColorPickerDialogList
}
}
@Override protected Object onGetDefaultValue(TypedArray a, int index) {
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInteger(index, Color.BLACK);
}
@Override public void onColorSelected(int dialogId, @ColorInt int color) {
@Override
public void onColorSelected(int dialogId, @ColorInt int color) {
saveValue(color);
}
@Override public void onDialogDismissed(int dialogId) {
@Override
public void onDialogDismissed(int dialogId) {
// no-op
}
/**
* Set the new color
*
* @param color
* The newly selected color
* @param color The newly selected color
*/
public void saveValue(@ColorInt int color) {
this.color = color;
@ -190,8 +209,7 @@ public class ColorPreference extends Preference implements ColorPickerDialogList
* Call {@link #saveValue(int)} after the user chooses a color.
* If this is set then it is up to you to show the dialog.
*
* @param listener
* The listener to show the dialog
* @param listener The listener to show the dialog
*/
public void setOnShowDialogListener(OnShowDialogListener listener) {
onShowDialogListener = listener;

View File

@ -1,6 +1,5 @@
#Wed May 05 20:20:56 JST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@ -44,10 +44,9 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
testImplementation "junit:junit:$junit_version"
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinx_coroutines_version"
}

View File

@ -1,21 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.juggler.apng.sample"
>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="jp.juggler.apng.sample">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<application
android:allowBackup="true"
android:fullBackupOnly="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
>
android:theme="@style/AppTheme">
<activity android:name=".ActList">
<activity
android:name=".ActList"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -78,21 +78,12 @@ class ActList : AppCompatActivity(), CoroutineScope {
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
PERMISSION_REQUEST_CODE_STORAGE -> {
// If request is cancelled, the result arrays are empty.
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
if( requestCode == PERMISSION_REQUEST_CODE_STORAGE){
if (grantResults.all{ it == PackageManager.PERMISSION_GRANTED }) {
// 特に何もしてないらしい
}
return
}
// other 'case' lines to check for other
// permissions this app might request
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
private fun load() = launch {

View File

@ -117,7 +117,7 @@ class ActViewer : AppCompatActivity() , CoroutineScope {
dir.mkdirs()
if(! dir.exists() ) {
Log.e(TAG, "Directory not exists: ${dir}")
Log.e(TAG, "Directory not exists: $dir")
return@launch
}
val frames = apngFrames.frames
@ -127,7 +127,7 @@ class ActViewer : AppCompatActivity() , CoroutineScope {
}
var i=0
for( f in frames) {
Log.d(TAG, "${title}[${i}] timeWidth=${f.timeWidth}")
Log.d(TAG, "$title[$i] timeWidth=${f.timeWidth}")
val bitmap = f.bitmap
FileOutputStream( File(dir,"${title}_${i}.png")).use{ fo ->