From 88e37d81f5898d6e0a8a337210f7d77e9a1033b0 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Wed, 27 Jan 2021 16:18:39 -0700 Subject: [PATCH 001/101] Remove Ruby --- .circleci/config.yml | 5 -- Gemfile | 4 - Gemfile.lock | 199 ------------------------------------------- 3 files changed, 208 deletions(-) delete mode 100644 Gemfile delete mode 100644 Gemfile.lock diff --git a/.circleci/config.yml b/.circleci/config.yml index b185da010..85d3ac10d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,11 +54,6 @@ jobs: api-version: "29" steps: - checkout - - bundle-install/bundle-install: - cache_key_prefix: installable-build - - run: - name: Copy Secrets - command: bundle exec fastlane run configure_apply - android/restore-gradle-cache - run: name: Build APK diff --git a/Gemfile b/Gemfile deleted file mode 100644 index d0def47fa..000000000 --- a/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source "https://rubygems.org" do - gem 'fastlane', '2.127.2' - gem 'fastlane-plugin-wpmreleasetoolkit', git: 'https://github.com/wordpress-mobile/release-toolkit', tag:'0.7.2' -end diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 25d7d2b12..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,199 +0,0 @@ -GIT - remote: https://github.com/wordpress-mobile/release-toolkit - revision: 21400012da8c2ceafa8ea2ce23d8a88233a97838 - tag: 0.7.2 - specs: - fastlane-plugin-wpmreleasetoolkit (0.7.2) - diffy (~> 3.3) - git (~> 1.3) - jsonlint - nokogiri (>= 1.10.4) - octokit (~> 4.13) - parallel (~> 1.14) - progress_bar (~> 1.3) - rake (~> 12.3) - rake-compiler (~> 1.0) - -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.1) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - atomos (0.1.3) - babosa (1.0.3) - claide (1.0.3) - colored (1.2) - colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - declarative (0.0.10) - declarative-option (0.1.0) - diffy (3.3.0) - digest-crc (0.4.1) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.5) - emoji_regex (1.0.1) - excon (0.67.0) - faraday (0.16.2) - multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) - http-cookie (~> 1.0.0) - faraday_middleware (0.13.1) - faraday (>= 0.7.4, < 1.0) - fastimage (2.1.7) - fastlane (2.127.2) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) - babosa (>= 1.0.2, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander-fastlane (>= 4.4.6, < 5.0.0) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 2.0) - excon (>= 0.45.0, < 1.0.0) - faraday (~> 0.9) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.9) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.21.2, < 0.24.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) - json (< 3.0.0) - jwt (~> 2.1.0) - mini_magick (>= 4.9.4, < 5.0.0) - multi_xml (~> 0.5) - multipart-post (~> 2.0.0) - plist (>= 3.1.0, < 4.0.0) - public_suffix (~> 2.0.0) - rubyzip (>= 1.2.2, < 2.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.8.1, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) - gh_inspector (1.1.3) - git (1.5.0) - google-api-client (0.23.9) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.5, < 0.7.0) - httpclient (>= 2.8.1, < 3.0) - mime-types (~> 3.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.9) - google-cloud-core (1.3.1) - google-cloud-env (~> 1.0) - google-cloud-env (1.2.1) - faraday (~> 0.11) - google-cloud-storage (1.16.0) - digest-crc (~> 0.4) - google-api-client (~> 0.23) - google-cloud-core (~> 1.2) - googleauth (>= 0.6.2, < 0.10.0) - googleauth (0.6.7) - faraday (~> 0.12) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (~> 0.7) - highline (1.7.10) - http-cookie (1.0.3) - domain_name (~> 0.5) - httpclient (2.8.3) - json (2.2.0) - jsonlint (0.3.0) - oj (~> 3) - optimist (~> 3) - jwt (2.1.0) - memoist (0.16.0) - mime-types (3.3) - mime-types-data (~> 3.2015) - mime-types-data (3.2019.0904) - mini_magick (4.9.5) - mini_portile2 (2.4.0) - multi_json (1.13.1) - multi_xml (0.6.0) - multipart-post (2.0.0) - nanaimo (0.2.6) - naturally (2.2.0) - nokogiri (1.10.4) - mini_portile2 (~> 2.4.0) - octokit (4.14.0) - sawyer (~> 0.8.0, >= 0.5.3) - oj (3.9.1) - optimist (3.0.0) - options (2.3.2) - os (1.0.1) - parallel (1.17.0) - plist (3.5.0) - progress_bar (1.3.0) - highline (>= 1.6, < 3) - options (~> 2.3.0) - public_suffix (2.0.5) - rake (12.3.3) - rake-compiler (1.0.8) - rake - representable (3.0.4) - declarative (< 0.1.0) - declarative-option (< 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rouge (2.0.7) - rubyzip (1.3.0) - sawyer (0.8.2) - addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) - security (0.1.3) - signet (0.11.0) - addressable (~> 2.3) - faraday (~> 0.9) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.6) - CFPropertyList - naturally - slack-notifier (2.3.2) - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - tty-cursor (0.7.0) - tty-screen (0.7.0) - tty-spinner (0.9.1) - tty-cursor (~> 0.7) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.6) - unicode-display_width (1.6.0) - word_wrap (1.0.0) - xcodeproj (1.12.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.2.6) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.0) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - ruby - -DEPENDENCIES - fastlane (= 2.127.2)! - fastlane-plugin-wpmreleasetoolkit! - -BUNDLED WITH - 1.17.3 From 13a00152baa22460a45e4dba638cb378c1fb2359 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Wed, 27 Jan 2021 22:23:52 -0700 Subject: [PATCH 002/101] Use the `configure` gradle plugin --- .circleci/config.yml | 3 +++ .configure | 6 +++--- build.gradle | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 85d3ac10d..c182bf308 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,6 +54,9 @@ jobs: api-version: "29" steps: - checkout + - run: + name: Apply Configuration + command: ./gradlew applyConfiguration - android/restore-gradle-cache - run: name: Build APK diff --git a/.configure b/.configure index eefab5f70..b62dd06f3 100644 --- a/.configure +++ b/.configure @@ -1,16 +1,16 @@ { "project_name": "stories-android", "branch": "master", - "pinned_hash": "746c07fd89399e5951493a32f2e853e804ca7fca", + "pinned_hash": "f97cfd2e1b6ed310c8a5a0e7caeb43658185f292", "files_to_copy": [ { "file": "android/Loop/gradle.properties", - "destination": "gradle.properties", + "destination": ".configure-files/gradle.properties", "encrypt": true }, { "file": "android/automattic.jks", - "destination": "app/release.jks", + "destination": ".configure-files/release.jks", "encrypt": true } ], diff --git a/build.gradle b/build.gradle index 2e5459990..33629e1b3 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { repositories { google() jcenter() - + maven { url "https://dl.bintray.com/automattic/maven/" } } dependencies { classpath 'com.android.tools.build:gradle:3.5.1' @@ -14,12 +14,15 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" classpath 'com.automattic.android:fetchstyle:1.1' classpath 'io.sentry:sentry-android-gradle-plugin:1.7.27' + classpath 'com.automattic.android:configure:0.3.0' + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } apply plugin: 'com.automattic.android.fetchstyle' +apply plugin: 'com.automattic.android.configure' allprojects { apply plugin: 'checkstyle' From f86939f54e4dc7e06bdfc19c11e5c85f02db9275 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Wed, 27 Jan 2021 23:05:46 -0700 Subject: [PATCH 003/101] Move configuration secrets into .configure-files/ Still supports using a `gradle.properties` file in the root of the project --- .configure | 2 +- .configure-files/gradle.properties.enc | Bin 1424 -> 1475 bytes .configure-files/release.jks.enc | Bin 0 -> 1332 bytes .gitignore | 4 ++++ app/build.gradle | 14 ++++---------- build.gradle | 14 ++++++++++++++ stories/build.gradle | 15 +++++---------- 7 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 .configure-files/release.jks.enc diff --git a/.configure b/.configure index b62dd06f3..6322ea781 100644 --- a/.configure +++ b/.configure @@ -1,7 +1,7 @@ { "project_name": "stories-android", "branch": "master", - "pinned_hash": "f97cfd2e1b6ed310c8a5a0e7caeb43658185f292", + "pinned_hash": "c8d5a863639968f824f2fb2a7e3e7616c828d521", "files_to_copy": [ { "file": "android/Loop/gradle.properties", diff --git a/.configure-files/gradle.properties.enc b/.configure-files/gradle.properties.enc index c9ae9bd81c5f093098a111ca0be5c577d52daf0f..06fee10cee662a5ef8441fe9d0d2949aa4750b5e 100644 GIT binary patch literal 1475 zcmV;!1w8s3_mG}WKsz!TO9cI^coso`6}ZR;L-1M5`Q&vW3^h^p1Z&nH9%K^QUqCo| zvmy51RUc=NUwbUEG>$U*D3GT7MU-OUd!FD5dpfz8YGL!!exCzZRr?`FswHX60tpu& zoTcD_alZT6G*L+Q&pv0P@SI0+qr7O$Ttd<$&wO^9)EX2_GV3`?#p%rd{l5F!P$yyd zWJs?tarUAU>Qf&8hhhcv1;V`Ye%K|WN7I}xQ)c>Y^DWy3d7r2u0%fFUyT8*nmK@5b z1y>({FzWZ_4iGwGgJOz;Bui|h^9lZmLhy^%iz!_*_e6)ww6{Ow+4Vl(%i)Uj-g;6r zr5LT@T1Sg$SjTGO!nWWM#^zt(8_-4cIi%wyw}tPPAaLa!`7W)>|BV+rgx3{a7?}mx zB(K`ql#O z^Z_Jiu^qByXc%J6o99q>s(!~S96FDPNM9;Jf%wV`0XPOEjs`h%0q;0w?U zt;V;(UTn1QP)qSB!H*3*aU_Ob8h02?I}hY&65}JSUBEtqf1{%WpnI|izLeO;4Xhsd zm69GTwbo+Z%?NwYb9t={tmhV>`CA3#KW^W#viWUhQ<6Ke7X<}c|9r}+YSivVXoJQr z_8ZJ2bIOS=^!a0D(*d?d)p(5nJ-;eRylVe@jSpswj=5|sjH9nx2>gj(?4}em*Io0;gq26>26AKQ>mv~eA+ptL%Ge*J6iELz`7t0o%7^u zo8^_w?)tQo2VmT#V`T*9i}R_<;Rr#qhVxdztA+j|A`8URx^F!=CoKPeuWTTB7o!Jf z!P8o-4m?21^(c#q6AAux&x%l=tKm191s~LPBzGa|qva~neL#O7qX0kP=)y65%VFqA zoZKf0AMoUq8*E(X#`539$IXb*L@~^Hit@XTjKr)2P%%)81E||ax~lz_@DW3oQ7N8^ zC5nmu7>$P2D&ZAm0vNi1D1;0js2zy#3&!w2c*c~q4oSVolO&6#4?$vR!6=R*gbGC^ zx9c{2J`8S1HXZ!VOwWwzbq`n7b=NU`Pa~xx7O*;GvWbqPdg($ii833@y#;Dw0?pY2 zFfJn$C94d{WywL`AfHu$a3T$na}jP4v?Kihlqsa^UL0^#-ARfAy}S5S85%ruTu%U> zUpDAIE^RM4jQNt%4z#yigHSbgNFgLL;`hg3vF~$hmOU?1gA3k4Cb+y`Re|(q6qo-6WSMHqrOQiOJ`mX}6 z6q`1lm%8SNr;6o}QdYorcmmIenKSnm;F?Fw|HJw5jsW@m%#tSp?UA1-nisA1M!VO& z8fal-J?@C^qeZ#?r5P4a#&*bDKc+IsUp^T$-8teSqni?%27y;+o3Db+v~J;f*41>y z)#2_6D?~N&-EYj$Hf5Avg?N^k4j(b13|{2{asSeBG92W~FjnQ+Xr$5zp=#v)2l)u_ zac!~LZ%vD*zY00q!;IJi^o$Nk>tblV)+aBJdn>5oMj?`i8=OxL(qN8bSf=$*vK&80 zRFEZS08l31I!$sHiV2QTIC5`cutD_Wyfr4T#tQkx63RpRHK`K-)?3XN?kZ~dzJRHq z)=w`BZY}B=t08|Lk>veT=*(?ep6K4{N*D1h++n(TmPD~ d-nr}9>$D}3me<*{#Gq#u)A_pbZ&V!bOrBXTwlAz)na8aT#8t=ji zCdalBPv8tQ<)I-2m%vK9`*2ml?8~rY8?Aq_!E6>oM0>vW57fUPMZqOqD~)#QTecC3 z)H^d|cI{6-a1&cedb{v?W0rPXYb0sM>Kb_DcOGUFuD4g5)`L4Q+qTj8G@k4)-`Ppk z5muX574X2WcZ~QW8yfo%GJOgsu;00)1;ol?;kzng+Bcg6O4p{KJSD#g4rs55;>fC< z&b&t+USbNP7fi4m=~-2l7SxcuO@3_p4LgY;LIbHw;DcJHo|4R%z`7))q-}%=0yB2) z)<`4u+S7v%J+4AL|o+ zKr4wL(4NNkI*%sJdbfUTY!mj@YDoaU52l^s8^n$ET`DSy-J1GTXEZQMQPd`*CigEv zb{SdMl};_DHz_pFhWTzvKpPB&bRVHaOQ=3bp;r8rBib(fX zf!!4@>VY}-g2XXl0jWiZ&6g3EQBg^Cm?qe1Q~tu;yOKKoSlX)DxF61~KUUYu@2aCs zeK)L$o^#{P%yQoreLfKhFjK7Hi3-8nEcO$`P*BWoRR~4_zxSR)mjyNn=YFf%Xm`xv zd_bpbhA*VB*X}vTRd7nh7w>VX;o`9W-aXA5%zw(qJLVc7dfAHuSlw|18OJB7g^C2^PHgq!gmb}iXQ zuMqzE7IzhVo-F+>$$ANYuH_;R@BlZ`{pVVv7H5_7n#N7HbUzLc-Nps!u3K$nvk650 z_l6<~sr)t~{t9VFtSJX>$Egj|YX9>sh8LMj*hxo-1E1@QB6t%<`xPZ8j7;7TI@^)< zPvH+7JS8~@>lv9j4fk)|(`oFQJaesxnezhM94ldtIH8r>8ZAw?nFm`e#okp$n`PiK zTlZVbqaKPO>{&U~bJ&7V{)t__UclH?Xs%LmSu?!VvLNTx&27H$NV65M|GD%YNPRlYzREHxhyo4{^K4BxS#=y6J`4^A=(15Gt?k;LO#mhYo zP^VEd653`1hl=lO_LK+^)c|rKYZZ1}y%YNg3^JBGWYhmL%yMbmS!i&Ff`o1a_}=@L zTKPq(fR~QX)4!GMf z*nW#(0*=M%VP<9EQugfbl6|)|${2mCk@{MyGsWmR%H~IF{Hs{+`}*TM`aGf_Fq3n? e@O=bXL7I)3&V>tfUv8$2|d^ss-@t#1Dg;a=LVWhauG_H(k z1n7gtT%b;{M0l5oL>Ds=QhpsIV9<+IPPT?6>Iba*pGZ@=l5$x+AoOdbbia{!^o}R4 z``(-jMPGvD{sD>ZPygK?;VY$nVTqU&Z5MBgUTiWcrx?rRA~ouIy-ERD;p|70v`ct* z#E(Iy>__hF#EF}v`Cl$TIWTA#pO|!mqZt=iV$Yju4dE{%mMt#M{AK1~4h9K1__ z&bUAqR$LGR2GKMEcA8!_cAhu7>%l3VluP%>y&Nx;UfkXh-<)o+Wad|R4 zKz3H@3-sm(T^mKk5h#~n={@W2MJY;+1Y| zeuhiL8>ynW|J}>scPh;h9RfFq^vgFfeK5`3tiREsmVVkl8dSf&NHlP7GSJeHWIMp&KbeD%~jruW?m3}M(= zz@^qcDzoOUstrcSM72?g7r~|X1m(I%Q86!H$PrLht7gkb&B~;@%@yP)l}BR!3q)MW zcO|M64^p^3^qHMCWCW?_)DI8f9e7S0KZF!<7TB&3dsyM?5$Aj~tqsIpclAf&Cw&dJ?8&1BqaB1G-2YL>(JW+>INPw@dzp>7){U#MEfy2mdb`6nqb8d>X?QZi)xhXV zzR{^k>gM<}83Y1b_LdBG@CrL2S`{ORV1=Nmc?Pg;g?jTs$FY_> z6`_?zkLt#{+Z07vOzky0sX`X1cMo1dLj8+WIju7#eDQbh@L=KQi#GWY*@l(bx)yT8 qMfK>qwy&hsm)wZ6dop!@Q_f%;n;ghtEBd|N)&Qbvi*jZ`T8_X}E0JIT literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index df22b9b56..b55d41cce 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,10 @@ gradle.properties # Local configuration file (sdk path, etc) local.properties +# Configuration Secrets +.configure-files/* +!.configure-files/*.enc + # Proguard folder generated by Eclipse proguard/ diff --git a/app/build.gradle b/app/build.gradle index b7824e988..4feb9e824 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,8 +84,10 @@ dependencies { android.buildTypes.all { buildType -> // Add properties named "loop.xxx" to our BuildConfig - def inputFile = checkGradlePropertiesFile() - def properties = loadPropertiesFromFile(inputFile) + if (!rootProject.gradleProperties.exists()) { + return + } + def properties = loadPropertiesFromFile(rootProject.gradleProperties) properties.any { property -> if (property.key.toLowerCase().startsWith("loop.")) { buildType.buildConfigField "String", property.key.replace("loop.", "").replace(".", "_").toUpperCase(), @@ -102,14 +104,6 @@ android.buildTypes.all { buildType -> } } -def checkGradlePropertiesFile() { - def inputFile = file("${rootDir}/gradle.properties") - if (!inputFile.exists()) { - throw new StopActionException("Build configuration file gradle.properties doesn't exist, follow README instructions") - } - return inputFile -} - static def loadPropertiesFromFile(inputFile) { def properties = new Properties() inputFile.withInputStream { stream -> diff --git a/build.gradle b/build.gradle index 33629e1b3..784f5949a 100644 --- a/build.gradle +++ b/build.gradle @@ -96,8 +96,22 @@ ext { appCompatVersion = '1.0.2' coreVersion = '1.2.0' navComponentVersion = '2.0.0' + + gradleProperties = findGradlePropertiesFile() } task clean(type: Delete) { delete rootProject.buildDir } + +// Find the gradle.properties file, preferring the one in `.configure-files`, but falling back to one in the project +// root for anyone without access to production secrets. +def findGradlePropertiesFile() { + + def configurePath = file("${rootDir}/.configure-files/gradle.properties") + if (configurePath.exists()) { + return configurePath + } + + return file("${rootDir}/gradle.properties") +} diff --git a/stories/build.gradle b/stories/build.gradle index 49580c71a..b5ec05e02 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -73,8 +73,11 @@ dependencies { android.buildTypes.all { buildType -> // Add properties named "loop.xxx" to our BuildConfig - def inputFile = checkGradlePropertiesFile() - def properties = loadPropertiesFromFile(inputFile) + if (!rootProject.gradleProperties.exists()) { + return + } + + def properties = loadPropertiesFromFile(rootProject.gradleProperties) properties.any { property -> if (property.key.toLowerCase().startsWith("wp.stories.use.")) { buildType.buildConfigField "boolean", property.key.replace("wp.stories.", "").replace(".", "_").toUpperCase(), @@ -96,14 +99,6 @@ android.buildTypes.all { buildType -> } } -def checkGradlePropertiesFile() { - def inputFile = file("${rootDir}/gradle.properties") - if (!inputFile.exists()) { - throw new StopActionException("Build configuration file gradle.properties doesn't exist, follow README instructions") - } - return inputFile -} - static def loadPropertiesFromFile(inputFile) { def properties = new Properties() inputFile.withInputStream { stream -> From 89f1db354f29e4f56bd801cabeff390b2bbc3f30 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Wed, 27 Jan 2021 22:52:18 -0700 Subject: [PATCH 004/101] Fix signing --- app/build.gradle | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4feb9e824..9a0e0733c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,6 +7,23 @@ apply plugin: 'io.sentry.android.gradle' android { compileSdkVersion rootProject.compileSdkVersion + signingConfigs { + release { + + if (!rootProject.gradleProperties.exists()) { + println "Unable to read ${gradleProperties}" + return + } + + def properties = loadPropertiesFromFile(rootProject.gradleProperties) + + storeFile file("${rootDir}/${properties.storeFile}") + storePassword properties.storePassword + keyAlias = properties.keyAlias + keyPassword properties.keyPassword + } + } + defaultConfig { applicationId "com.automattic.loop" minSdkVersion rootProject.minSdkVersion @@ -19,6 +36,7 @@ android { versionName "1.0.0.14-prealpha" } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + signingConfig signingConfigs.release } buildTypes { release { @@ -111,18 +129,3 @@ static def loadPropertiesFromFile(inputFile) { } return properties } - -// For app signing -if (["storeFile", "storePassword", "keyAlias", "keyPassword"].count { !project.hasProperty(it) } == 0) { - android { - signingConfigs { - release { - storeFile = rootProject.file(project.storeFile) - storePassword = project.storePassword - keyAlias = project.keyAlias - keyPassword = project.keyPassword - } - } - } - android.buildTypes.release.signingConfig = android.signingConfigs.release -} From 7df28b5d08b99a2070ffa4d41a583144417607b5 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Mon, 1 Feb 2021 16:02:29 -0700 Subject: [PATCH 005/101] Remove the bundle-install orb --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c182bf308..409eb62bf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,6 @@ orbs: # Using 1.0 of the Orbs means it will use the latest 1.0.x version from https://github.com/wordpress-mobile/circleci-orbs android: wordpress-mobile/android@1.0 git: wordpress-mobile/git@1.0 - bundle-install: toshimaru/bundle-install@0.3.1 slack: circleci/slack@3.4.2 commands: From 3682896985a582c00176ed26d1d39878bdd92c85 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Mon, 1 Feb 2021 16:08:52 -0700 Subject: [PATCH 006/101] Use gradle.properties in the project root --- .configure | 2 +- .gitignore | 3 +++ build.gradle | 14 +------------- ...gradle.properties.enc => gradle.properties.enc | Bin 4 files changed, 5 insertions(+), 14 deletions(-) rename .configure-files/gradle.properties.enc => gradle.properties.enc (100%) diff --git a/.configure b/.configure index 6322ea781..4964b8e62 100644 --- a/.configure +++ b/.configure @@ -5,7 +5,7 @@ "files_to_copy": [ { "file": "android/Loop/gradle.properties", - "destination": ".configure-files/gradle.properties", + "destination": "gradle.properties", "encrypt": true }, { diff --git a/.gitignore b/.gitignore index b55d41cce..22d3a1ded 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ local.properties .configure-files/* !.configure-files/*.enc +# Ignore auto-created gradle.properties backups from `./gradlew updateConfiguration` +gradle*.properties.bak + # Proguard folder generated by Eclipse proguard/ diff --git a/build.gradle b/build.gradle index 784f5949a..27892b6e5 100644 --- a/build.gradle +++ b/build.gradle @@ -97,21 +97,9 @@ ext { coreVersion = '1.2.0' navComponentVersion = '2.0.0' - gradleProperties = findGradlePropertiesFile() + gradleProperties = file("${rootDir}/gradle.properties") } task clean(type: Delete) { delete rootProject.buildDir } - -// Find the gradle.properties file, preferring the one in `.configure-files`, but falling back to one in the project -// root for anyone without access to production secrets. -def findGradlePropertiesFile() { - - def configurePath = file("${rootDir}/.configure-files/gradle.properties") - if (configurePath.exists()) { - return configurePath - } - - return file("${rootDir}/gradle.properties") -} diff --git a/.configure-files/gradle.properties.enc b/gradle.properties.enc similarity index 100% rename from .configure-files/gradle.properties.enc rename to gradle.properties.enc From 54d450c772ae2b568a9256a8fa3314e54e7e4fd9 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Mon, 1 Feb 2021 21:45:52 -0500 Subject: [PATCH 007/101] Add loadProperties method and refactor release signingConfigs --- app/build.gradle | 28 +++++----------------------- build.gradle | 15 +++++++++++++-- stories/build.gradle | 13 +------------ 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9a0e0733c..b336d6835 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,18 +9,10 @@ android { signingConfigs { release { - - if (!rootProject.gradleProperties.exists()) { - println "Unable to read ${gradleProperties}" - return - } - - def properties = loadPropertiesFromFile(rootProject.gradleProperties) - - storeFile file("${rootDir}/${properties.storeFile}") - storePassword properties.storePassword - keyAlias = properties.keyAlias - keyPassword properties.keyPassword + storeFile file("${rootDir}/${rootProject.storeFile}") + storePassword rootProject.storePassword + keyAlias rootProject.keyAlias + keyPassword rootProject.keyPassword } } @@ -102,10 +94,7 @@ dependencies { android.buildTypes.all { buildType -> // Add properties named "loop.xxx" to our BuildConfig - if (!rootProject.gradleProperties.exists()) { - return - } - def properties = loadPropertiesFromFile(rootProject.gradleProperties) + def properties = rootProject.loadGradleProperties() properties.any { property -> if (property.key.toLowerCase().startsWith("loop.")) { buildType.buildConfigField "String", property.key.replace("loop.", "").replace(".", "_").toUpperCase(), @@ -122,10 +111,3 @@ android.buildTypes.all { buildType -> } } -static def loadPropertiesFromFile(inputFile) { - def properties = new Properties() - inputFile.withInputStream { stream -> - properties.load(stream) - } - return properties -} diff --git a/build.gradle b/build.gradle index 27892b6e5..289e72848 100644 --- a/build.gradle +++ b/build.gradle @@ -96,10 +96,21 @@ ext { appCompatVersion = '1.0.2' coreVersion = '1.2.0' navComponentVersion = '2.0.0' - - gradleProperties = file("${rootDir}/gradle.properties") } task clean(type: Delete) { delete rootProject.buildDir } + +def loadGradleProperties() { + def inputFile = file("${rootDir}/gradle.properties") + if (!inputFile.exists()) { + throw new StopActionException("Build configuration file gradle.properties doesn't exist, follow README instructions") + } + def properties = new Properties() + inputFile.withInputStream { stream -> + properties.load(stream) + } + return properties +} + diff --git a/stories/build.gradle b/stories/build.gradle index b5ec05e02..922300786 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -73,11 +73,7 @@ dependencies { android.buildTypes.all { buildType -> // Add properties named "loop.xxx" to our BuildConfig - if (!rootProject.gradleProperties.exists()) { - return - } - - def properties = loadPropertiesFromFile(rootProject.gradleProperties) + def properties = rootProject.loadGradleProperties() properties.any { property -> if (property.key.toLowerCase().startsWith("wp.stories.use.")) { buildType.buildConfigField "boolean", property.key.replace("wp.stories.", "").replace(".", "_").toUpperCase(), @@ -99,10 +95,3 @@ android.buildTypes.all { buildType -> } } -static def loadPropertiesFromFile(inputFile) { - def properties = new Properties() - inputFile.withInputStream { stream -> - properties.load(stream) - } - return properties -} From c29c553179967222621452eaed6b5702a7fc3649 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Mon, 1 Feb 2021 22:38:29 -0500 Subject: [PATCH 008/101] Use task avoidance to load properties --- app/build.gradle | 46 +++++++++++++++++++++++++++----------------- build.gradle | 1 - stories/build.gradle | 37 ++++++++++++++++++----------------- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b336d6835..0baa0da55 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,10 +9,16 @@ android { signingConfigs { release { - storeFile file("${rootDir}/${rootProject.storeFile}") - storePassword rootProject.storePassword - keyAlias rootProject.keyAlias - keyPassword rootProject.keyPassword + if (rootProject.hasProperty("storeFile") + && rootProject.hasProperty("storePassword") + && rootProject.hasProperty("keyAlias") + && rootProject.hasProperty("keyPassword") + ) { + storeFile file("${rootDir}/${rootProject.storeFile}") + storePassword rootProject.storePassword + keyAlias rootProject.keyAlias + keyPassword rootProject.keyPassword + } } } @@ -92,22 +98,26 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } -android.buildTypes.all { buildType -> +tasks.register('createBuildConfigFieldsFromProperties') { // Add properties named "loop.xxx" to our BuildConfig - def properties = rootProject.loadGradleProperties() - properties.any { property -> - if (property.key.toLowerCase().startsWith("loop.")) { - buildType.buildConfigField "String", property.key.replace("loop.", "").replace(".", "_").toUpperCase(), - "\"${property.value}\"" - } - else if (property.key.toLowerCase().startsWith("sentry.dsn")) { - buildType.buildConfigField "String", property.key.replace(".", "_").toUpperCase(), - "\"${property.value}\"" - } - else if (property.key.toLowerCase().startsWith("loop.res.")) { - buildType.resValue "string", property.key.replace("loop.res.", "").replace(".", "_").toLowerCase(), - "${property.value}" + android.buildTypes.all { buildType -> + def properties = rootProject.loadGradleProperties() + properties.any { property -> + if (property.key.toLowerCase().startsWith("loop.")) { + buildType.buildConfigField "String", property.key.replace("loop.", "").replace(".", "_").toUpperCase(), + "\"${property.value}\"" + } + else if (property.key.toLowerCase().startsWith("sentry.dsn")) { + buildType.buildConfigField "String", property.key.replace(".", "_").toUpperCase(), + "\"${property.value}\"" + } + else if (property.key.toLowerCase().startsWith("loop.res.")) { + buildType.resValue "string", property.key.replace("loop.res.", "").replace(".", "_").toLowerCase(), + "${property.value}" + } } } } +preBuild.dependsOn(tasks.named("createBuildConfigFieldsFromProperties")) + diff --git a/build.gradle b/build.gradle index 289e72848..afeeedb2d 100644 --- a/build.gradle +++ b/build.gradle @@ -113,4 +113,3 @@ def loadGradleProperties() { } return properties } - diff --git a/stories/build.gradle b/stories/build.gradle index 922300786..869160088 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -71,27 +71,28 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } -android.buildTypes.all { buildType -> +tasks.register('createBuildConfigFieldsFromProperties') { // Add properties named "loop.xxx" to our BuildConfig - def properties = rootProject.loadGradleProperties() - properties.any { property -> - if (property.key.toLowerCase().startsWith("wp.stories.use.")) { - buildType.buildConfigField "boolean", property.key.replace("wp.stories.", "").replace(".", "_").toUpperCase(), - "${property.value}" + android.buildTypes.all { buildType -> + def properties = rootProject.loadGradleProperties() + properties.any { property -> + if (property.key.toLowerCase().startsWith("wp.stories.use.")) { + buildType.buildConfigField "boolean", property.key.replace("wp.stories.", "").replace(".", "_").toUpperCase(), "${property.value}" + } + else if (property.key.toLowerCase().startsWith("wp.stories.")) { + buildType.buildConfigField "String", property.key.replace("wp.stories.", "").replace(".", "_").toUpperCase(), "\"${property.value}\"" + } + else if (property.key.toLowerCase().startsWith("wp.stories.res.")) { + buildType.resValue "string", property.key.replace("wp.stories.res.", "").replace(".", "_").toLowerCase(), "${property.value}" + } } - else if (property.key.toLowerCase().startsWith("wp.stories.")) { - buildType.buildConfigField "String", property.key.replace("wp.stories.", "").replace(".", "_").toUpperCase(), - "\"${property.value}\"" - } - else if (property.key.toLowerCase().startsWith("wp.stories.res.")) { - buildType.resValue "string", property.key.replace("wp.stories.res.", "").replace(".", "_").toLowerCase(), - "${property.value}" - } - } - if (properties.getProperty("wp.stories.use.cameraX") == null) { - // use cameraX implementation by default if no gradle.properties set - buildType.buildConfigField "boolean", "USE_CAMERAX", "true" + if (properties.getProperty("wp.stories.use.cameraX") == null) { + // use cameraX implementation by default if no gradle.properties set + buildType.buildConfigField "boolean", "USE_CAMERAX", "true" + } } } +preBuild.dependsOn(tasks.named("createBuildConfigFieldsFromProperties")) + From bd755cd7fc13802a418c8a7d58e0e04d7c31d5e5 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Fri, 5 Feb 2021 12:36:47 -0700 Subject: [PATCH 009/101] Avoid unncessary changes --- app/build.gradle | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0baa0da55..6131bd688 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,21 +7,6 @@ apply plugin: 'io.sentry.android.gradle' android { compileSdkVersion rootProject.compileSdkVersion - signingConfigs { - release { - if (rootProject.hasProperty("storeFile") - && rootProject.hasProperty("storePassword") - && rootProject.hasProperty("keyAlias") - && rootProject.hasProperty("keyPassword") - ) { - storeFile file("${rootDir}/${rootProject.storeFile}") - storePassword rootProject.storePassword - keyAlias rootProject.keyAlias - keyPassword rootProject.keyPassword - } - } - } - defaultConfig { applicationId "com.automattic.loop" minSdkVersion rootProject.minSdkVersion @@ -121,3 +106,17 @@ tasks.register('createBuildConfigFieldsFromProperties') { preBuild.dependsOn(tasks.named("createBuildConfigFieldsFromProperties")) +// For app signing +if (["storeFile", "storePassword", "keyAlias", "keyPassword"].count { !project.hasProperty(it) } == 0) { + android { + signingConfigs { + release { + storeFile = rootProject.file(project.storeFile) + storePassword = project.storePassword + keyAlias = project.keyAlias + keyPassword = project.keyPassword + } + } + } + android.buildTypes.release.signingConfig = android.signingConfigs.release +} \ No newline at end of file From cd0c4c78ae663b974d6cd6140ef27c46539866d2 Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Fri, 5 Feb 2021 12:37:29 -0700 Subject: [PATCH 010/101] =?UTF-8?q?=E2=80=A6and=20add=20a=20trailing=20new?= =?UTF-8?q?line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6131bd688..dae37289b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -119,4 +119,4 @@ if (["storeFile", "storePassword", "keyAlias", "keyPassword"].count { !project.h } } android.buildTypes.release.signingConfig = android.signingConfigs.release -} \ No newline at end of file +} From 82e8ae27485ce8c515d62a7ff2ba6e77c692811c Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Fri, 5 Feb 2021 12:40:48 -0700 Subject: [PATCH 011/101] Apply task avoidance to app signing --- app/build.gradle | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index dae37289b..d951910fc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -102,21 +102,21 @@ tasks.register('createBuildConfigFieldsFromProperties') { } } } -} - -preBuild.dependsOn(tasks.named("createBuildConfigFieldsFromProperties")) -// For app signing -if (["storeFile", "storePassword", "keyAlias", "keyPassword"].count { !project.hasProperty(it) } == 0) { - android { - signingConfigs { - release { - storeFile = rootProject.file(project.storeFile) - storePassword = project.storePassword - keyAlias = project.keyAlias - keyPassword = project.keyPassword + // For app signing + if (["storeFile", "storePassword", "keyAlias", "keyPassword"].count { !project.hasProperty(it) } == 0) { + android { + signingConfigs { + release { + storeFile = rootProject.file(project.storeFile) + storePassword = project.storePassword + keyAlias = project.keyAlias + keyPassword = project.keyPassword + } } } + android.buildTypes.release.signingConfig = android.signingConfigs.release } - android.buildTypes.release.signingConfig = android.signingConfigs.release } + +preBuild.dependsOn(tasks.named("createBuildConfigFieldsFromProperties")) From 7595cd00cd7379d1c284ccc15f1a97648f9da91b Mon Sep 17 00:00:00 2001 From: Jeremy Massel Date: Fri, 5 Feb 2021 12:47:17 -0700 Subject: [PATCH 012/101] Revert Changes --- app/build.gradle | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d951910fc..0baa0da55 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,6 +7,21 @@ apply plugin: 'io.sentry.android.gradle' android { compileSdkVersion rootProject.compileSdkVersion + signingConfigs { + release { + if (rootProject.hasProperty("storeFile") + && rootProject.hasProperty("storePassword") + && rootProject.hasProperty("keyAlias") + && rootProject.hasProperty("keyPassword") + ) { + storeFile file("${rootDir}/${rootProject.storeFile}") + storePassword rootProject.storePassword + keyAlias rootProject.keyAlias + keyPassword rootProject.keyPassword + } + } + } + defaultConfig { applicationId "com.automattic.loop" minSdkVersion rootProject.minSdkVersion @@ -102,21 +117,7 @@ tasks.register('createBuildConfigFieldsFromProperties') { } } } - - // For app signing - if (["storeFile", "storePassword", "keyAlias", "keyPassword"].count { !project.hasProperty(it) } == 0) { - android { - signingConfigs { - release { - storeFile = rootProject.file(project.storeFile) - storePassword = project.storePassword - keyAlias = project.keyAlias - keyPassword = project.keyPassword - } - } - } - android.buildTypes.release.signingConfig = android.signingConfigs.release - } } preBuild.dependsOn(tasks.named("createBuildConfigFieldsFromProperties")) + From 67e333d588ffbd8ab2e3389c3c9adde2d6de1fa8 Mon Sep 17 00:00:00 2001 From: Alex Forcier Date: Fri, 12 Feb 2021 09:47:06 +0900 Subject: [PATCH 013/101] Move font loading to background task and preload --- .../compose/ComposeLoopFrameActivity.kt | 3 + .../stories/compose/text/FontRequester.kt | 72 +++++++++++++++++++ .../compose/text/TextStyleGroupManager.kt | 24 ++++--- 3 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index db2919f1c..0aee94575 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -341,6 +341,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } + // Pre-load the custom fonts if necessary + TextStyleGroupManager.preloadFonts(this) + workingAreaRect = calculateWorkingArea() photoEditor = PhotoEditor.Builder(this, photoEditorView) .setPinchTextScalable(true) // set flag to make text scalable when pinch diff --git a/stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt b/stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt new file mode 100644 index 000000000..f3bd70096 --- /dev/null +++ b/stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt @@ -0,0 +1,72 @@ +package com.wordpress.stories.compose.text + +import android.content.Context +import android.content.res.Resources.NotFoundException +import android.graphics.Typeface +import android.util.Log +import androidx.annotation.FontRes +import androidx.core.content.res.ResourcesCompat +import androidx.core.content.res.ResourcesCompat.FontCallback +import androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason + +/** + * Handles dispatching requests to ResourcesCompat.getFont() asynchronously. These requests can take a long time, + * for example when the font is downloadable and there are connection issues. + * + * When registering or requesting a font from FontRequester, a fallback Typeface must be specified which will + * be returned if: + * - The request to ResourcesCompat hasn't completed yet + * - The request to ResourcesCompat failed for any reason + * - The font being requested wasn't previously registered with FontRequester + */ +object FontRequester { + private val TAG = FontRequester::class.java.simpleName + + val fontMap = mutableMapOf() + + fun registerFont(context: Context, @FontRes fontRes: Int, fallback: Typeface) { + // Set the font to the fallback right away in case it's requested before getFont completes. + fontMap[fontRes] = fallback + try { + ResourcesCompat.getFont(context, fontRes, fontCallbackFor(context, fontRes), null) + } catch (e: NotFoundException) { + Log.e(TAG, "Font ${context.resources.getResourceEntryName(fontRes)} not found") + } + } + + fun getFont(@FontRes fontRes: Int, fallback: Typeface): Typeface { + return fontMap[fontRes] ?: fallback + } + + private fun fontCallbackFor(context: Context, @FontRes fontRes: Int): FontCallback { + return object : FontCallback() { + /** + * Called when an asynchronous font was finished loading. + * + * @param typeface The font that was loaded. + */ + override fun onFontRetrieved(typeface: Typeface) { + fontMap[fontRes] = typeface + } + + /** + * Called when an asynchronous font failed to load. + * + * @param reason The reason the font failed to load. One of + * [FontRequestFailReason.FAIL_REASON_PROVIDER_NOT_FOUND], + * [FontRequestFailReason.FAIL_REASON_WRONG_CERTIFICATES], + * [FontRequestFailReason.FAIL_REASON_FONT_LOAD_ERROR], + * [FontRequestFailReason.FAIL_REASON_SECURITY_VIOLATION], + * [FontRequestFailReason.FAIL_REASON_FONT_NOT_FOUND], + * [FontRequestFailReason.FAIL_REASON_FONT_UNAVAILABLE] or + * [FontRequestFailReason.FAIL_REASON_MALFORMED_QUERY]. + */ + @Suppress("KDocUnresolvedReference") + override fun onFontRetrievalFailed(@FontRequestFailReason reason: Int) { + // Just log the failure - the font mapping is already pointed to the fallback font. + Log.e(TAG, "Font retrieval for ${context.resources.getResourceEntryName(fontRes)} failed. " + + "FontRequestFailReason error code: $reason.") + } + } + } +} diff --git a/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt b/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt index ae1203f2d..f81a3ac6e 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt @@ -1,7 +1,6 @@ package com.wordpress.stories.compose.text import android.content.Context -import android.content.res.Resources.NotFoundException import android.graphics.Typeface import android.util.TypedValue import android.widget.TextView @@ -12,7 +11,6 @@ import androidx.annotation.Dimension.SP import androidx.annotation.FontRes import androidx.annotation.StringRes import androidx.core.content.ContextCompat -import androidx.core.content.res.ResourcesCompat import com.automattic.photoeditor.text.IdentifiableTypeface import com.automattic.photoeditor.text.IdentifiableTypeface.TypefaceId import com.automattic.photoeditor.text.RoundedBackgroundColorSpan @@ -219,6 +217,20 @@ class TextStyleGroupManager(val context: Context) { return IdentifiableTypeface(typefaceId, resolveTypeface(typefaceId, context)) } + /** + * Initializes the custom fonts, including possibly downloading some of them for the first time. + * This is so they're ready when the text editor is eventually opened and we don't have to block + * the main thread or use the fallback fonts. + */ + fun preloadFonts(context: Context) { + FontRequester.registerFont(context, R.font.nunito_bold, Typeface.SANS_SERIF) + FontRequester.registerFont(context, R.font.libre_baskerville, Typeface.SERIF) + FontRequester.registerFont(context, R.font.oswald_upper, Typeface.SANS_SERIF) + FontRequester.registerFont(context, R.font.pacifico, Typeface.SERIF) + FontRequester.registerFont(context, R.font.space_mono_bold, Typeface.MONOSPACE) + FontRequester.registerFont(context, R.font.shrikhand, Typeface.DEFAULT_BOLD) + } + private fun resolveTypeface(@TypefaceId typefaceId: Int, context: Context): Typeface? { return when (typefaceId) { TYPEFACE_ID_NUNITO -> getFontWithFallback(context, R.font.nunito_bold, Typeface.SANS_SERIF) @@ -231,12 +243,8 @@ class TextStyleGroupManager(val context: Context) { } } - private fun getFontWithFallback(context: Context, @FontRes fontRes: Int, fallback: Typeface?): Typeface? { - return try { - ResourcesCompat.getFont(context, fontRes) - } catch (e: NotFoundException) { - fallback - } + private fun getFontWithFallback(context: Context, @FontRes fontRes: Int, fallback: Typeface): Typeface { + return FontRequester.getFont(fontRes, fallback) } } } From c5fcde3157b3fca86f1aa60a322bb55747bee5d0 Mon Sep 17 00:00:00 2001 From: Alex Forcier Date: Fri, 12 Feb 2021 09:48:20 +0900 Subject: [PATCH 014/101] Drop unneeded context parameters --- .../compose/ComposeLoopFrameActivity.kt | 2 +- .../compose/text/TextStyleGroupManager.kt | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 0aee94575..627257e83 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -430,7 +430,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec photoEditor.setFontResolver(object : FontResolver { override fun resolve(@TypefaceId typefaceId: Int): IdentifiableTypeface { - return TextStyleGroupManager.getIdentifiableTypefaceForId(typefaceId, this@ComposeLoopFrameActivity) + return TextStyleGroupManager.getIdentifiableTypefaceForId(typefaceId) } }) diff --git a/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt b/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt index f81a3ac6e..009792e3a 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/text/TextStyleGroupManager.kt @@ -103,7 +103,7 @@ class TextStyleGroupManager(val context: Context) { ) } - private fun getFont(@TypefaceId typefaceId: Int) = resolveTypeface(typefaceId, context) + private fun getFont(@TypefaceId typefaceId: Int) = resolveTypeface(typefaceId) private fun getString(@StringRes stringRes: Int) = context.resources.getString(stringRes) @@ -213,8 +213,8 @@ class TextStyleGroupManager(val context: Context) { const val TYPEFACE_ID_SPACE_MONO = 1005 const val TYPEFACE_ID_SHRIKHAND = 1006 - fun getIdentifiableTypefaceForId(@TypefaceId typefaceId: Int, context: Context): IdentifiableTypeface { - return IdentifiableTypeface(typefaceId, resolveTypeface(typefaceId, context)) + fun getIdentifiableTypefaceForId(@TypefaceId typefaceId: Int): IdentifiableTypeface { + return IdentifiableTypeface(typefaceId, resolveTypeface(typefaceId)) } /** @@ -231,19 +231,19 @@ class TextStyleGroupManager(val context: Context) { FontRequester.registerFont(context, R.font.shrikhand, Typeface.DEFAULT_BOLD) } - private fun resolveTypeface(@TypefaceId typefaceId: Int, context: Context): Typeface? { + private fun resolveTypeface(@TypefaceId typefaceId: Int): Typeface? { return when (typefaceId) { - TYPEFACE_ID_NUNITO -> getFontWithFallback(context, R.font.nunito_bold, Typeface.SANS_SERIF) - TYPEFACE_ID_LIBRE_BASKERVILLE -> getFontWithFallback(context, R.font.libre_baskerville, Typeface.SERIF) - TYPEFACE_ID_OSWALD -> getFontWithFallback(context, R.font.oswald_upper, Typeface.SANS_SERIF) - TYPEFACE_ID_PACIFICO -> getFontWithFallback(context, R.font.pacifico, Typeface.SERIF) - TYPEFACE_ID_SPACE_MONO -> getFontWithFallback(context, R.font.space_mono_bold, Typeface.MONOSPACE) - TYPEFACE_ID_SHRIKHAND -> getFontWithFallback(context, R.font.shrikhand, Typeface.DEFAULT_BOLD) + TYPEFACE_ID_NUNITO -> getFontWithFallback(R.font.nunito_bold, Typeface.SANS_SERIF) + TYPEFACE_ID_LIBRE_BASKERVILLE -> getFontWithFallback(R.font.libre_baskerville, Typeface.SERIF) + TYPEFACE_ID_OSWALD -> getFontWithFallback(R.font.oswald_upper, Typeface.SANS_SERIF) + TYPEFACE_ID_PACIFICO -> getFontWithFallback(R.font.pacifico, Typeface.SERIF) + TYPEFACE_ID_SPACE_MONO -> getFontWithFallback(R.font.space_mono_bold, Typeface.MONOSPACE) + TYPEFACE_ID_SHRIKHAND -> getFontWithFallback(R.font.shrikhand, Typeface.DEFAULT_BOLD) else -> null } } - private fun getFontWithFallback(context: Context, @FontRes fontRes: Int, fallback: Typeface): Typeface { + private fun getFontWithFallback(@FontRes fontRes: Int, fallback: Typeface): Typeface { return FontRequester.getFont(fontRes, fallback) } } From 5e7948675de95f557e2e1bc2634a2c43d1228434 Mon Sep 17 00:00:00 2001 From: Alex Forcier Date: Sat, 13 Feb 2021 11:07:41 +0900 Subject: [PATCH 015/101] Wrap font map in synchronized map --- .../java/com/wordpress/stories/compose/text/FontRequester.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt b/stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt index f3bd70096..d99309765 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/text/FontRequester.kt @@ -8,6 +8,7 @@ import androidx.annotation.FontRes import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat.FontCallback import androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason +import java.util.Collections /** * Handles dispatching requests to ResourcesCompat.getFont() asynchronously. These requests can take a long time, @@ -22,7 +23,7 @@ import androidx.core.provider.FontsContractCompat.FontRequestCallback.FontReques object FontRequester { private val TAG = FontRequester::class.java.simpleName - val fontMap = mutableMapOf() + val fontMap: MutableMap = Collections.synchronizedMap(mutableMapOf()) fun registerFont(context: Context, @FontRes fontRes: Int, fallback: Typeface) { // Set the font to the fallback right away in case it's requested before getFont completes. From 281adbb2b5f7dfab7e2b1e10dc39063221899cd5 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 15 Feb 2021 09:51:24 -0300 Subject: [PATCH 016/101] make loadGradleProperties method static so it can be used in diffeernt rootProjects --- app/build.gradle | 3 ++- build.gradle | 3 +-- stories/build.gradle | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0baa0da55..cd5c0b268 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -101,7 +101,8 @@ dependencies { tasks.register('createBuildConfigFieldsFromProperties') { // Add properties named "loop.xxx" to our BuildConfig android.buildTypes.all { buildType -> - def properties = rootProject.loadGradleProperties() + def inputFile = file("${rootDir}/gradle.properties") + def properties = loadGradleProperties(inputFile) properties.any { property -> if (property.key.toLowerCase().startsWith("loop.")) { buildType.buildConfigField "String", property.key.replace("loop.", "").replace(".", "_").toUpperCase(), diff --git a/build.gradle b/build.gradle index afeeedb2d..682ad87b6 100644 --- a/build.gradle +++ b/build.gradle @@ -102,8 +102,7 @@ task clean(type: Delete) { delete rootProject.buildDir } -def loadGradleProperties() { - def inputFile = file("${rootDir}/gradle.properties") +static def loadGradleProperties(inputFile) { if (!inputFile.exists()) { throw new StopActionException("Build configuration file gradle.properties doesn't exist, follow README instructions") } diff --git a/stories/build.gradle b/stories/build.gradle index 869160088..2b7b34ddf 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -74,7 +74,8 @@ dependencies { tasks.register('createBuildConfigFieldsFromProperties') { // Add properties named "loop.xxx" to our BuildConfig android.buildTypes.all { buildType -> - def properties = rootProject.loadGradleProperties() + def inputFile = file("${rootDir}/gradle.properties") + def properties = loadGradleProperties(inputFile) properties.any { property -> if (property.key.toLowerCase().startsWith("wp.stories.use.")) { buildType.buildConfigField "boolean", property.key.replace("wp.stories.", "").replace(".", "_").toUpperCase(), "${property.value}" From 157ed6c014b8076175002c6cbf2e9b2225fbbd29 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 17 Feb 2021 09:05:17 -0300 Subject: [PATCH 017/101] defined a common gralde file that contains a method which is exported through converting method to closure in ext --- app/build.gradle | 1 + build.gradle | 11 ----------- stories-common.gradle | 14 ++++++++++++++ stories/build.gradle | 1 + 4 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 stories-common.gradle diff --git a/app/build.gradle b/app/build.gradle index cd5c0b268..8d7ee80a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' apply plugin: 'io.sentry.android.gradle' +apply from: "../stories-common.gradle" android { compileSdkVersion rootProject.compileSdkVersion diff --git a/build.gradle b/build.gradle index 682ad87b6..33629e1b3 100644 --- a/build.gradle +++ b/build.gradle @@ -101,14 +101,3 @@ ext { task clean(type: Delete) { delete rootProject.buildDir } - -static def loadGradleProperties(inputFile) { - if (!inputFile.exists()) { - throw new StopActionException("Build configuration file gradle.properties doesn't exist, follow README instructions") - } - def properties = new Properties() - inputFile.withInputStream { stream -> - properties.load(stream) - } - return properties -} diff --git a/stories-common.gradle b/stories-common.gradle new file mode 100644 index 000000000..97b8e14f8 --- /dev/null +++ b/stories-common.gradle @@ -0,0 +1,14 @@ +def loadGradleProperties(inputFile) { + if (!inputFile.exists()) { + throw new StopActionException("Build configuration file gradle.properties doesn't exist, follow README instructions") + } + def properties = new Properties() + inputFile.withInputStream { stream -> + properties.load(stream) + } + return properties +} + +ext { + loadGradleProperties = this.&loadGradleProperties +} diff --git a/stories/build.gradle b/stories/build.gradle index 2b7b34ddf..f8261b93c 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' apply plugin: 'kotlinx-serialization' +apply from: "../stories-common.gradle" android { compileSdkVersion rootProject.compileSdkVersion From 3bcb4f7deaa5f948b2b1c80f9f4176a56392d2cf Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 17 Feb 2021 09:57:01 -0300 Subject: [PATCH 018/101] made method def static --- stories-common.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories-common.gradle b/stories-common.gradle index 97b8e14f8..f426aa368 100644 --- a/stories-common.gradle +++ b/stories-common.gradle @@ -1,4 +1,4 @@ -def loadGradleProperties(inputFile) { +static def loadGradleProperties(inputFile) { if (!inputFile.exists()) { throw new StopActionException("Build configuration file gradle.properties doesn't exist, follow README instructions") } From 74997dd62a574be0bc6132749e93b65e536b082a Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 23 Feb 2021 14:02:03 -0300 Subject: [PATCH 019/101] using Chris Banes' PhotoView for the BackgroundImageView, to provide panning / pinch to zoom --- build.gradle | 2 ++ photoeditor/build.gradle | 2 ++ .../photoeditor/views/background/fixed/BackgroundImageView.kt | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 33629e1b3..92c92e372 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ buildscript { google() jcenter() maven { url "https://dl.bintray.com/automattic/maven/" } + maven { url "https://jitpack.io" } } dependencies { classpath 'com.android.tools.build:gradle:3.5.1' @@ -30,6 +31,7 @@ allprojects { repositories { google() jcenter() + maven { url "https://jitpack.io" } } if (tasks.findByPath('checkstyle') == null) { diff --git a/photoeditor/build.gradle b/photoeditor/build.gradle index f9f107303..a5f9ac119 100644 --- a/photoeditor/build.gradle +++ b/photoeditor/build.gradle @@ -54,6 +54,8 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.10.0' kapt 'com.github.bumptech.glide:compiler:4.10.0' + implementation 'com.github.chrisbanes:PhotoView:2.3.0' + implementation project(path: ':mp4compose') lintChecks 'org.wordpress:lint:1.0.1' diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt index ada546983..090d72972 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt @@ -10,14 +10,14 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.net.Uri import android.util.AttributeSet -import androidx.appcompat.widget.AppCompatImageView +import com.github.chrisbanes.photoview.PhotoView /** * @author [Burhanuddin Rashid](https://github.com/burhanrashid52) * @version 0.1.2 * @since 5/21/2018 */ -internal class BackgroundImageView : AppCompatImageView { +internal class BackgroundImageView : PhotoView { private var mOnImageChangedListener: OnImageChangedListener? = null val bitmap: Bitmap? From ca897d726cc75d6a51b911dfa0461fbc06ba4a88 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 23 Feb 2021 20:16:09 -0300 Subject: [PATCH 020/101] save the original ImageMatrix and set it on the ghostPhotoView after loading the image drawable on it in preparePhotoEditorViewForSnapshot when saving the image frame to disk --- .../stories/compose/frame/FrameSaveManager.kt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index cbb3f2538..78418e493 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -3,6 +3,7 @@ package com.wordpress.stories.compose.frame import android.content.Context import android.graphics.Bitmap import android.graphics.Color +import android.graphics.Matrix import android.net.Uri import android.view.View import android.view.ViewGroup.LayoutParams @@ -142,8 +143,9 @@ class FrameSaveManager( } else { try { // create ghost PhotoEditorView to be used for saving off-screen + val originalMatrix = photoEditor.composedCanvas.source.imageMatrix val ghostPhotoEditorView = createGhostPhotoEditor(context, photoEditor.composedCanvas) - frameFile = saveImageFrame(context, frame, ghostPhotoEditorView, frameIndex) + frameFile = saveImageFrame(context, frame, ghostPhotoEditorView, originalMatrix, frameIndex) frame.composedFrameFile = frameFile saveProgressListener?.onFrameSaveCompleted(frameIndex, frame) } catch (ex: Exception) { @@ -167,10 +169,11 @@ class FrameSaveManager( context: Context, frame: StoryFrameItem, ghostPhotoEditorView: PhotoEditorView, + originalMatrix: Matrix, frameIndex: FrameIndex ): File { // prepare the ghostview with its background image and the AddedViews on top of it - val futureTarget = preparePhotoEditorViewForSnapshot(context, frame, ghostPhotoEditorView) + val futureTarget = preparePhotoEditorViewForSnapshot(context, frame, originalMatrix, ghostPhotoEditorView) val file = withContext(Dispatchers.IO) { if (normalizeTo916 && !isSizeRatio916(ghostPhotoEditorView.width, ghostPhotoEditorView.height)) { @@ -276,21 +279,26 @@ class FrameSaveManager( private suspend fun preparePhotoEditorViewForSnapshot( context: Context, frame: StoryFrameItem, + originalMatrix: Matrix, ghostPhotoEditorView: PhotoEditorView ): FutureTarget { // prepare background val uri = (frame.source as? UriBackgroundSource)?.contentUri ?: (frame.source as FileBackgroundSource).file + val targetView = ghostPhotoEditorView.source + // making use of Glide to decode bitmap and get the right orientation automatically // http://bumptech.github.io/glide/doc/getting-started.html#background-threads val futureTarget = Glide.with(context) .asBitmap() .load(uri) .transform(CenterCrop()) // also use CenterCrop as it's the same the user was seeing as per WYSIWYG - .submit(ghostPhotoEditorView.source.measuredWidth, ghostPhotoEditorView.source.measuredHeight) + .submit(targetView.measuredWidth, targetView.measuredHeight) val bitmap = futureTarget.get() - ghostPhotoEditorView.source.setImageBitmap(bitmap) + targetView.setImageBitmap(bitmap) + + targetView.imageMatrix = originalMatrix // reset old matrix // removeViewFromParent for views that were added in the UI thread need to also run on the main thread // otherwise we'd get a android.view.ViewRootImpl$CalledFromWrongThreadException: From 50a78bfdb477bd24bf2565e6b93f62c8acaf2f2f Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 15:49:30 -0300 Subject: [PATCH 021/101] added BackgroundViewInfo data class to remember backing support PhotoView zoom matrix when switching frames --- .../background/fixed/BackgroundImageView.kt | 2 +- stories/build.gradle | 2 + .../compose/ComposeLoopFrameActivity.kt | 68 +++++++++++++++++++ .../stories/compose/story/StoryFrameItem.kt | 7 ++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt index 090d72972..b60d64d34 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt @@ -17,7 +17,7 @@ import com.github.chrisbanes.photoview.PhotoView * @version 0.1.2 * @since 5/21/2018 */ -internal class BackgroundImageView : PhotoView { +class BackgroundImageView : PhotoView { private var mOnImageChangedListener: OnImageChangedListener? = null val bitmap: Bitmap? diff --git a/stories/build.gradle b/stories/build.gradle index f8261b93c..779778fdd 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -63,6 +63,8 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" + implementation 'com.github.chrisbanes:PhotoView:2.3.0' + lintChecks 'org.wordpress:lint:1.0.1' testImplementation 'junit:junit:4.12' diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 627257e83..daf83583f 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -9,8 +9,10 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection +import android.graphics.drawable.Drawable import android.graphics.Rect import android.graphics.drawable.ColorDrawable +import android.graphics.Matrix import android.hardware.Camera import android.media.MediaScannerConnection import android.net.Uri @@ -63,8 +65,13 @@ import com.automattic.photoeditor.views.ViewType import com.automattic.photoeditor.views.ViewType.TEXT import com.automattic.photoeditor.views.added.AddedViewList import com.bumptech.glide.Glide +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target +import com.github.chrisbanes.photoview.PhotoView import com.wordpress.stories.BuildConfig import com.wordpress.stories.R import com.wordpress.stories.compose.ComposeLoopFrameActivity.ExternalMediaPickerRequestCodesAndExtraKeys @@ -90,6 +97,7 @@ import com.wordpress.stories.compose.story.StoryFrameItem import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.FileBackgroundSource import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.UriBackgroundSource +import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundViewInfo import com.wordpress.stories.compose.story.StoryFrameItemType import com.wordpress.stories.compose.story.StoryFrameItemType.IMAGE import com.wordpress.stories.compose.story.StoryFrameItemType.VIDEO @@ -1873,6 +1881,22 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec addCurrentViewsToFrameAtIndex(oldIndex) } + // save current imageMatrix as the background image may have been resized + val oldSelectedFrame = storyViewModel.getSelectedFrame() + if (oldSelectedFrame?.frameItemType is IMAGE) { + val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView + val matrixValues = FloatArray(9) + // backgroundImageSource.imageMatrix.getValues(matrixValues) + val matrix = Matrix() + // fill in matrix with PhotoView Support matrix + backgroundImageSource.getSuppMatrix(matrix) + // extract matrix to float array matrixValues + matrix.getValues(matrixValues) + oldSelectedFrame.source.backgroundViewInfo = BackgroundViewInfo( + imageMatrixValues = matrixValues + ) + } // TODO add else clause and handle VIDEO frameItemType + // This is tricky. See https://stackoverflow.com/questions/45860434/cant-remove-view-from-root-view // we need to disable layout transition animations so changes in views' parent are set // immediately. Otherwise a view's parent will only change once the animation ends, and hence @@ -1903,7 +1927,51 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec Glide.with(this@ComposeLoopFrameActivity) .load(model) .transform(CenterCrop()) + .listener(object : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + // let the default implementation run + return false + } + + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + // here setup the PhotoView support matrix + val handler = Handler() + // we use a handler because we need to set the support matrix only once the drawable + // has been set on the PhotoView, otherwise the matrix is not applied + // see + // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 + handler.post { + val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView + val backgroundViewInfo = newSelectedFrame.source.backgroundViewInfo + // load image matrix from data if it exists + backgroundViewInfo?.let { + val matrix = Matrix() + matrix.setValues(it.imageMatrixValues) + backgroundImageSource.apply { + // imageMatrix.setValues(it.imageMatrixValues) + setSuppMatrix(matrix) + // setDisplayMatrix(matrix) + // invalidate() + } + } + } + // return false to let Glide proceed and set the drawable + return false + } + }) .into(photoEditorView.source) + showStaticBackground() } diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt index f47fb9370..cad42a54a 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt @@ -27,8 +27,15 @@ data class StoryFrameItem( var composedFrameFile: File? = null, var id: String? = null ) { + + @Serializable + data class BackgroundViewInfo( + val imageMatrixValues: FloatArray + ) + @Serializable sealed class BackgroundSource { + var backgroundViewInfo: BackgroundViewInfo? = null @Serializable data class UriBackgroundSource( @Serializable(with = UriSerializer::class) From 47f5053e02942f9ade0a69989ae434b8e1caaa9f Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 15:55:50 -0300 Subject: [PATCH 022/101] moved RequestListener out to a function to make code more readable --- .../compose/ComposeLoopFrameActivity.kt | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index daf83583f..475c38034 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -1925,52 +1925,21 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } else { val model = (source as? FileBackgroundSource)?.file ?: (source as UriBackgroundSource).contentUri Glide.with(this@ComposeLoopFrameActivity) - .load(model) - .transform(CenterCrop()) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - // let the default implementation run - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - // here setup the PhotoView support matrix - val handler = Handler() - // we use a handler because we need to set the support matrix only once the drawable - // has been set on the PhotoView, otherwise the matrix is not applied - // see - // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 - handler.post { - val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView - val backgroundViewInfo = newSelectedFrame.source.backgroundViewInfo - // load image matrix from data if it exists - backgroundViewInfo?.let { - val matrix = Matrix() - matrix.setValues(it.imageMatrixValues) - backgroundImageSource.apply { - // imageMatrix.setValues(it.imageMatrixValues) - setSuppMatrix(matrix) - // setDisplayMatrix(matrix) - // invalidate() - } - } + .load(model) + .transform(CenterCrop()) + .listener(provideGlideRequestListenerWithHandler { + val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView + val backgroundViewInfo = newSelectedFrame.source.backgroundViewInfo + // load image matrix from data if it exists + backgroundViewInfo?.let { + val matrix = Matrix() + matrix.setValues(it.imageMatrixValues) + backgroundImageSource.apply { + setSuppMatrix(matrix) } - // return false to let Glide proceed and set the drawable - return false } }) - .into(photoEditorView.source) + .into(photoEditorView.source) showStaticBackground() } @@ -1990,6 +1959,37 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec showRetryButtonAndHideEditControlsForErroredFrame(newSelectedFrame.saveResultReason !is SaveSuccess) } + private fun provideGlideRequestListenerWithHandler(setupPhotoViewMatrix: Runnable) : RequestListener { + return object : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + // let the default implementation run + return false + } + + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + // here setup the PhotoView support matrix + // we use a handler because we need to set the support matrix only once the drawable + // has been set on the PhotoView, otherwise the matrix is not applied + // see + // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 + Handler().post(setupPhotoViewMatrix) + // return false to let Glide proceed and set the drawable + return false + } + } + } + override fun onStoryFrameAddTapped() { addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) showMediaPicker() From 4eca102b0336157077f62a2398d940a13f2b1e31 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 17:09:14 -0300 Subject: [PATCH 023/101] use getCurrentStoryFrameAt(oldIndex) instead of getSelectedFrame when selection has just changed, because the current selected index is now no longer the oldIndex --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 475c38034..adf58c5b6 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -1882,7 +1882,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } // save current imageMatrix as the background image may have been resized - val oldSelectedFrame = storyViewModel.getSelectedFrame() + val oldSelectedFrame = storyViewModel.getCurrentStoryFrameAt(oldIndex) if (oldSelectedFrame?.frameItemType is IMAGE) { val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView val matrixValues = FloatArray(9) From ff08278d55aa568be9644b0da5f61c09ecf65e7d Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 17:31:10 -0300 Subject: [PATCH 024/101] using the bbackgroundViewInfo array to built the imageMatrix when saving IMAGE based frames --- .../com/wordpress/stories/compose/frame/FrameSaveManager.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index 78418e493..26a789ee7 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -143,7 +143,11 @@ class FrameSaveManager( } else { try { // create ghost PhotoEditorView to be used for saving off-screen - val originalMatrix = photoEditor.composedCanvas.source.imageMatrix + val originalMatrix = Matrix() + frame.source.backgroundViewInfo?.let { + originalMatrix.setValues(it.imageMatrixValues) + } + val ghostPhotoEditorView = createGhostPhotoEditor(context, photoEditor.composedCanvas) frameFile = saveImageFrame(context, frame, ghostPhotoEditorView, originalMatrix, frameIndex) frame.composedFrameFile = frameFile From a0fec095b6cd050d22132ab51768e01bb13c24d5 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 17:52:29 -0300 Subject: [PATCH 025/101] removed commented code line --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index adf58c5b6..04d0e9df2 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -1886,7 +1886,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (oldSelectedFrame?.frameItemType is IMAGE) { val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView val matrixValues = FloatArray(9) - // backgroundImageSource.imageMatrix.getValues(matrixValues) val matrix = Matrix() // fill in matrix with PhotoView Support matrix backgroundImageSource.getSuppMatrix(matrix) From 49fa468c72ba7e4177d882848a45f93566307bb5 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 18:11:43 -0300 Subject: [PATCH 026/101] fixed lint warning' --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 04d0e9df2..cca9b386f 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -1958,7 +1958,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec showRetryButtonAndHideEditControlsForErroredFrame(newSelectedFrame.saveResultReason !is SaveSuccess) } - private fun provideGlideRequestListenerWithHandler(setupPhotoViewMatrix: Runnable) : RequestListener { + private fun provideGlideRequestListenerWithHandler(setupPhotoViewMatrix: Runnable): RequestListener { return object : RequestListener { override fun onLoadFailed( e: GlideException?, From c5a9dabaa0dfc4361589f64a36494b2bdc77a463 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 18:19:21 -0300 Subject: [PATCH 027/101] encapsulated code in new methods setBackgroundViewInfoOnFrame and setBackgroundViewInfoOnPhotoView --- .../compose/ComposeLoopFrameActivity.kt | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index cca9b386f..e85f7c608 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -1884,15 +1884,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // save current imageMatrix as the background image may have been resized val oldSelectedFrame = storyViewModel.getCurrentStoryFrameAt(oldIndex) if (oldSelectedFrame?.frameItemType is IMAGE) { - val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView - val matrixValues = FloatArray(9) - val matrix = Matrix() - // fill in matrix with PhotoView Support matrix - backgroundImageSource.getSuppMatrix(matrix) - // extract matrix to float array matrixValues - matrix.getValues(matrixValues) - oldSelectedFrame.source.backgroundViewInfo = BackgroundViewInfo( - imageMatrixValues = matrixValues + setBackgroundViewInfoOnFrame( + oldSelectedFrame, + photoEditor.composedCanvas.source as PhotoView ) } // TODO add else clause and handle VIDEO frameItemType @@ -1927,16 +1921,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec .load(model) .transform(CenterCrop()) .listener(provideGlideRequestListenerWithHandler { - val backgroundImageSource = photoEditor.composedCanvas.source as PhotoView - val backgroundViewInfo = newSelectedFrame.source.backgroundViewInfo - // load image matrix from data if it exists - backgroundViewInfo?.let { - val matrix = Matrix() - matrix.setValues(it.imageMatrixValues) - backgroundImageSource.apply { - setSuppMatrix(matrix) - } - } + setBackgroundViewInfoOnPhotoView( + newSelectedFrame, + photoEditor.composedCanvas.source as PhotoView + ) }) .into(photoEditorView.source) @@ -1958,6 +1946,30 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec showRetryButtonAndHideEditControlsForErroredFrame(newSelectedFrame.saveResultReason !is SaveSuccess) } + private fun setBackgroundViewInfoOnFrame(frame: StoryFrameItem, backgroundImageSource: PhotoView) { + val matrixValues = FloatArray(9) + val matrix = Matrix() + // fill in matrix with PhotoView Support matrix + backgroundImageSource.getSuppMatrix(matrix) + // extract matrix to float array matrixValues + matrix.getValues(matrixValues) + frame.source.backgroundViewInfo = BackgroundViewInfo( + imageMatrixValues = matrixValues + ) + } + + private fun setBackgroundViewInfoOnPhotoView(frame: StoryFrameItem, backgroundImageSource: PhotoView) { + val backgroundViewInfo = frame.source.backgroundViewInfo + // load image matrix from data if it exists + backgroundViewInfo?.let { + val matrix = Matrix() + matrix.setValues(it.imageMatrixValues) + backgroundImageSource.apply { + setSuppMatrix(matrix) + } + } + } + private fun provideGlideRequestListenerWithHandler(setupPhotoViewMatrix: Runnable): RequestListener { return object : RequestListener { override fun onLoadFailed( From 1963373f584a920d242848a18da2dae7418fffc5 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 18:20:59 -0300 Subject: [PATCH 028/101] removed empty line after brace --- .../java/com/wordpress/stories/compose/story/StoryFrameItem.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt index cad42a54a..5f5c1dca3 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt @@ -27,7 +27,6 @@ data class StoryFrameItem( var composedFrameFile: File? = null, var id: String? = null ) { - @Serializable data class BackgroundViewInfo( val imageMatrixValues: FloatArray From a35fc3b3349d93fff04f8c694d49bb1d26298e72 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 24 Feb 2021 18:43:25 -0300 Subject: [PATCH 029/101] moved oldSelectedFrame getter code into index boundary check --- .../compose/ComposeLoopFrameActivity.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index e85f7c608..c85b11e92 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -1879,16 +1879,16 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (oldIndex >= 0) { // only remember added views for frame if current index is valid addCurrentViewsToFrameAtIndex(oldIndex) - } - // save current imageMatrix as the background image may have been resized - val oldSelectedFrame = storyViewModel.getCurrentStoryFrameAt(oldIndex) - if (oldSelectedFrame?.frameItemType is IMAGE) { - setBackgroundViewInfoOnFrame( - oldSelectedFrame, - photoEditor.composedCanvas.source as PhotoView - ) - } // TODO add else clause and handle VIDEO frameItemType + // save current imageMatrix as the background image may have been resized + val oldSelectedFrame = storyViewModel.getCurrentStoryFrameAt(oldIndex) + if (oldSelectedFrame?.frameItemType is IMAGE) { + setBackgroundViewInfoOnFrame( + oldSelectedFrame, + photoEditor.composedCanvas.source as PhotoView + ) + } // TODO add else clause and handle VIDEO frameItemType + } // This is tricky. See https://stackoverflow.com/questions/45860434/cant-remove-view-from-root-view // we need to disable layout transition animations so changes in views' parent are set From b951f97ae231182208dfff81ea108f9e53c49556 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 1 Mar 2021 16:21:30 -0300 Subject: [PATCH 030/101] handling FIT_CENTER scaleType both in edit mode and in FrameSaveManager --- .../photoeditor/views/PhotoEditorView.kt | 4 ++-- .../stories/compose/ComposeLoopFrameActivity.kt | 14 +++++++++++++- .../stories/compose/frame/FrameSaveManager.kt | 9 +++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index 637168fa6..d539dc143 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -12,7 +12,7 @@ import android.view.TextureView.SurfaceTextureListener import android.view.View import android.view.ViewGroup import android.widget.ImageView -import android.widget.ImageView.ScaleType.CENTER_CROP +import android.widget.ImageView.ScaleType.FIT_CENTER import android.widget.ProgressBar import android.widget.RelativeLayout import androidx.annotation.RequiresApi @@ -109,7 +109,7 @@ class PhotoEditorView : RelativeLayout { backgroundImage = BackgroundImageView(context).apply { id = imgSrcId adjustViewBounds = true - scaleType = CENTER_CROP + scaleType = FIT_CENTER } val imgSrcParam = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT).apply { diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index c85b11e92..8a157eb83 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -67,6 +67,7 @@ import com.automattic.photoeditor.views.added.AddedViewList import com.bumptech.glide.Glide import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.load.resource.bitmap.FitCenter import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestListener @@ -1116,6 +1117,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun saveStoryPreHook() { showLoading() + refreshBackgroundViewInfoOnSelectedFrame() // disable layout change animations, we need this to make added views immediately visible, otherwise // we may end up capturing a Bitmap of a backing drawable that still has not been updated // (i.e. no visible added Views) @@ -1124,6 +1126,15 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec preHookRun = true } + private fun refreshBackgroundViewInfoOnSelectedFrame() { + storyViewModel.getSelectedFrame()?.let { + setBackgroundViewInfoOnFrame( + it, + photoEditor.composedCanvas.source as PhotoView + ) + } + } + private fun saveStoryPostHook(result: StorySaveResult) { doUnbindService() // re-enable layout change animations @@ -1919,7 +1930,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val model = (source as? FileBackgroundSource)?.file ?: (source as UriBackgroundSource).contentUri Glide.with(this@ComposeLoopFrameActivity) .load(model) - .transform(CenterCrop()) + .transform(FitCenter()) .listener(provideGlideRequestListenerWithHandler { setBackgroundViewInfoOnPhotoView( newSelectedFrame, @@ -2003,6 +2014,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec override fun onStoryFrameAddTapped() { addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) + refreshBackgroundViewInfoOnSelectedFrame() showMediaPicker() } diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index 26a789ee7..bdec4d0eb 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -7,11 +7,13 @@ import android.graphics.Matrix import android.net.Uri import android.view.View import android.view.ViewGroup.LayoutParams +import android.widget.ImageView.ScaleType.FIT_CENTER import android.widget.RelativeLayout import com.automattic.photoeditor.PhotoEditor import com.automattic.photoeditor.PhotoEditor.OnSaveWithCancelAndProgressListener import com.automattic.photoeditor.views.PhotoEditorView import com.automattic.photoeditor.views.ViewType.STICKER_ANIMATED +import com.automattic.photoeditor.views.background.fixed.BackgroundImageView import com.wordpress.stories.compose.story.StoryFrameItem import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.FileBackgroundSource import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.UriBackgroundSource @@ -22,6 +24,7 @@ import com.wordpress.stories.util.cloneViewSpecs import com.wordpress.stories.util.removeViewFromParent import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.FitCenter import com.bumptech.glide.request.FutureTarget import com.wordpress.stories.util.isSizeRatio916 import com.wordpress.stories.util.normalizeSizeExportTo916 @@ -297,12 +300,14 @@ class FrameSaveManager( val futureTarget = Glide.with(context) .asBitmap() .load(uri) - .transform(CenterCrop()) // also use CenterCrop as it's the same the user was seeing as per WYSIWYG .submit(targetView.measuredWidth, targetView.measuredHeight) val bitmap = futureTarget.get() targetView.setImageBitmap(bitmap) - targetView.imageMatrix = originalMatrix // reset old matrix + (targetView as BackgroundImageView).apply { + scaleType = FIT_CENTER + setSuppMatrix(originalMatrix) + } // removeViewFromParent for views that were added in the UI thread need to also run on the main thread // otherwise we'd get a android.view.ViewRootImpl$CalledFromWrongThreadException: From cb057c00e798f3fa3c41df4f0de14df78628250b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 1 Mar 2021 16:42:26 -0300 Subject: [PATCH 031/101] removed unused imports, added clarifying comment --- .../com/wordpress/stories/compose/frame/FrameSaveManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index bdec4d0eb..5993deca5 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -23,8 +23,6 @@ import com.wordpress.stories.compose.story.StoryFrameItemType.VIDEO import com.wordpress.stories.util.cloneViewSpecs import com.wordpress.stories.util.removeViewFromParent import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.bitmap.CenterCrop -import com.bumptech.glide.load.resource.bitmap.FitCenter import com.bumptech.glide.request.FutureTarget import com.wordpress.stories.util.isSizeRatio916 import com.wordpress.stories.util.normalizeSizeExportTo916 @@ -304,6 +302,8 @@ class FrameSaveManager( val bitmap = futureTarget.get() targetView.setImageBitmap(bitmap) + // IMPORTANT: scaleType and setSuppMatrix should only be called _after_ the bitmap is set on the targetView + // by means of targetView.setImageBitmap(). Calling this before will have no effect due to PhotoView's checks. (targetView as BackgroundImageView).apply { scaleType = FIT_CENTER setSuppMatrix(originalMatrix) From 19066d57106e4fb5d62adb055a6a0ff514e1ebc2 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 2 Mar 2021 08:24:50 -0300 Subject: [PATCH 032/101] limit workingArea in calculateWorkingArea() to the same size as exported output --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 8a157eb83..caf32f3be 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -117,6 +117,7 @@ import com.wordpress.stories.util.STATE_KEY_CURRENT_STORY_INDEX import com.wordpress.stories.util.getDisplayPixelSize import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle import com.wordpress.stories.util.isVideo +import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.android.synthetic.main.activity_composer.* import kotlinx.android.synthetic.main.content_composer.* import kotlinx.coroutines.CoroutineScope @@ -312,14 +313,16 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val width = photoEditorView.measuredWidth val height = photoEditorView.measuredHeight + val normalizedScreenSize = normalizeSizeExportTo916(width, height).toSize() + val bottomAreaHeight = resources.getDimensionPixelSize(R.dimen.bottom_strip_height) + bottomNavigationBarMargin val topAreaHeight = resources.getDimensionPixelSize(R.dimen.edit_mode_button_size) return Rect( xCoord, yCoord + topAreaHeight, - xCoord + width, - yCoord + height - bottomAreaHeight + xCoord + normalizedScreenSize.width, + yCoord + normalizedScreenSize.height - bottomAreaHeight ) } From c7dea6c9fb429558a34aa348babda358edc7abff Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 2 Mar 2021 20:26:24 -0300 Subject: [PATCH 033/101] using jp.wasabeef:glide-transformations library to add blurried image background --- photoeditor/build.gradle | 2 ++ .../photoeditor/views/PhotoEditorView.kt | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/photoeditor/build.gradle b/photoeditor/build.gradle index a5f9ac119..95cc93b7c 100644 --- a/photoeditor/build.gradle +++ b/photoeditor/build.gradle @@ -54,6 +54,8 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.10.0' kapt 'com.github.bumptech.glide:compiler:4.10.0' + implementation 'jp.wasabeef:glide-transformations:4.3.0' + implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation project(path: ':mp4compose') diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index d539dc143..8b7e4c9d5 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -12,10 +12,12 @@ import android.view.TextureView.SurfaceTextureListener import android.view.View import android.view.ViewGroup import android.widget.ImageView +import android.widget.ImageView.ScaleType.CENTER_CROP import android.widget.ImageView.ScaleType.FIT_CENTER import android.widget.ProgressBar import android.widget.RelativeLayout import androidx.annotation.RequiresApi +import androidx.appcompat.widget.AppCompatImageView import com.automattic.photoeditor.OnSaveBitmap import com.automattic.photoeditor.R.styleable import com.automattic.photoeditor.views.background.fixed.BackgroundImageView @@ -24,6 +26,9 @@ import com.automattic.photoeditor.views.brush.BrushDrawingView import com.automattic.photoeditor.views.filter.CustomEffect import com.automattic.photoeditor.views.filter.ImageFilterView import com.automattic.photoeditor.views.filter.PhotoFilter +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import jp.wasabeef.glide.transformations.BlurTransformation /** * @@ -40,9 +45,11 @@ import com.automattic.photoeditor.views.filter.PhotoFilter class PhotoEditorView : RelativeLayout { private lateinit var autoFitTextureView: AutoFitTextureView private lateinit var backgroundImage: BackgroundImageView + private lateinit var backgroundImageBlurred: AppCompatImageView private lateinit var brushDrawingView: BrushDrawingView private lateinit var imageFilterView: ImageFilterView private lateinit var progressBar: ProgressBar + private var attachedToWindow: Boolean = false private var surfaceListeners: ArrayList = ArrayList() @@ -103,8 +110,24 @@ class PhotoEditorView : RelativeLayout { init(attrs) } + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + attachedToWindow = false + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + attachedToWindow = true + } + @SuppressLint("Recycle") private fun init(attrs: AttributeSet?) { + backgroundImageBlurred = AppCompatImageView(context).apply { + id = imgBlurSrcId + adjustViewBounds = true + scaleType = CENTER_CROP + } + // Setup image attributes backgroundImage = BackgroundImageView(context).apply { id = imgSrcId @@ -159,6 +182,11 @@ class PhotoEditorView : RelativeLayout { backgroundImage.setOnImageChangedListener(object : BackgroundImageView.OnImageChangedListener { override fun onBitmapLoaded(sourceBitmap: Bitmap?) { + if (attachedToWindow) { + Glide.with(context).load(sourceBitmap) + .apply(RequestOptions.bitmapTransform(BlurTransformation(25, 3))) + .into(backgroundImageBlurred) + } imageFilterView.setFilterEffect(PhotoFilter.NONE) imageFilterView.setSourceBitmap(sourceBitmap) Log.d(TAG, "onBitmapLoaded() called with: sourceBitmap = [$sourceBitmap]") @@ -179,6 +207,9 @@ class PhotoEditorView : RelativeLayout { // Add camera preview addView(autoFitTextureView, cameraParam) + // Add image source + addView(backgroundImageBlurred, imgSrcParam) + // Add image source addView(backgroundImage, imgSrcParam) @@ -286,6 +317,7 @@ class PhotoEditorView : RelativeLayout { companion object { private val TAG = "PhotoEditorView" + private val imgBlurSrcId = 5 private val imgSrcId = 1 private val brushSrcId = 2 private val glFilterId = 3 From 43755837b440b2d5d0f2ab73f4f12b605ff7c252 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 3 Mar 2021 13:34:13 -0300 Subject: [PATCH 034/101] added an opaque bottom bar on top of background views to match the normalizedExportedSize on screen, for handsets with an aspect ratio taller than 9:16 --- app/src/main/res/values/colors.xml | 1 + .../photoeditor/views/PhotoEditorView.kt | 29 ++++++++++++++++ .../compose/ComposeLoopFrameActivity.kt | 34 +++++++++++++++++++ .../story/StoryFrameSelectorFragment.kt | 11 ++++++ .../com/wordpress/stories/util/ViewUtils.kt | 4 +++ stories/src/main/res/values/colors.xml | 1 + 6 files changed, 80 insertions(+) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 87ec31ecf..a7d628acf 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -23,6 +23,7 @@ #99000000 #4D000000 @color/black_transp_light + @color/black @color/black_transp_light @color/black_transp_light_darker #4D000000 diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index d539dc143..b44778159 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -3,6 +3,7 @@ package com.automattic.photoeditor.views import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap +import android.graphics.Color import android.graphics.SurfaceTexture import android.os.Build import android.util.AttributeSet @@ -17,6 +18,7 @@ import android.widget.ProgressBar import android.widget.RelativeLayout import androidx.annotation.RequiresApi import com.automattic.photoeditor.OnSaveBitmap +import com.automattic.photoeditor.R import com.automattic.photoeditor.R.styleable import com.automattic.photoeditor.views.background.fixed.BackgroundImageView import com.automattic.photoeditor.views.background.video.AutoFitTextureView @@ -39,6 +41,7 @@ import com.automattic.photoeditor.views.filter.PhotoFilter class PhotoEditorView : RelativeLayout { private lateinit var autoFitTextureView: AutoFitTextureView + private lateinit var bottomOpaqueBar: View private lateinit var backgroundImage: BackgroundImageView private lateinit var brushDrawingView: BrushDrawingView private lateinit var imageFilterView: ImageFilterView @@ -176,6 +179,16 @@ class PhotoEditorView : RelativeLayout { addRule(ALIGN_BOTTOM, imgSrcId) } + // Setup image attributes + bottomOpaqueBar = View(context).apply { + id = bottomOpaqueBarId + setBackgroundColor(Color.parseColor("#000000")) + } + + val bottomOpaqueBarParams = LayoutParams(LayoutParams.MATCH_PARENT, 0).apply { + addRule(ALIGN_PARENT_BOTTOM, TRUE) + } + // Add camera preview addView(autoFitTextureView, cameraParam) @@ -190,6 +203,9 @@ class PhotoEditorView : RelativeLayout { // Add progress view addView(progressBar, progressBarParam) + + // finally add the opaque bar that will be drawn for FIT_SCALE positioning on taller than 9:16 screens + addView(bottomOpaqueBar, bottomOpaqueBarParams) } // added this method as a helper due to the reasons outlined here: @@ -212,6 +228,18 @@ class PhotoEditorView : RelativeLayout { parent.addView(textureView, index, cameraParam) } + fun setOpaqueBarHeight(height: Int) { + bottomOpaqueBar.layoutParams.height = height + } + + fun hideOpaqueBar() { + bottomOpaqueBar.visibility = View.INVISIBLE + } + + fun showOpaqueBar() { + bottomOpaqueBar.visibility = View.VISIBLE + } + internal fun saveFilter(onSaveBitmap: OnSaveBitmap) { // check which background is currently visible: if it's // - imageFilterView: a filter has been applied, process it first @@ -286,6 +314,7 @@ class PhotoEditorView : RelativeLayout { companion object { private val TAG = "PhotoEditorView" + private val bottomOpaqueBarId = 6 private val imgSrcId = 1 private val brushSrcId = 2 private val glFilterId = 3 diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index caf32f3be..49dd99963 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -25,6 +25,7 @@ import android.os.Vibrator import android.provider.Settings import android.text.TextUtils import android.util.Log +import android.util.TypedValue import android.view.GestureDetector import android.view.Gravity import android.view.MotionEvent @@ -116,10 +117,12 @@ import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT import com.wordpress.stories.util.STATE_KEY_CURRENT_STORY_INDEX import com.wordpress.stories.util.getDisplayPixelSize import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle +import com.wordpress.stories.util.isScreenTallerThan916 import com.wordpress.stories.util.isVideo import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.android.synthetic.main.activity_composer.* import kotlinx.android.synthetic.main.content_composer.* +import kotlinx.android.synthetic.main.fragment_story_frame_selector.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -200,6 +203,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private lateinit var backgroundSurfaceManager: BackgroundSurfaceManager private var currentOriginalCapturedFile: File? = null private lateinit var workingAreaRect: Rect + private var bottomOpaqueBarHeight: Int = 0 // default: no opaque bottom bar private val timesUpRunnable = Runnable { stopRecordingVideo(false) // time's up, it's not a cancellation @@ -341,6 +345,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec bottomNavigationBarMargin = insets.systemWindowInsetBottom workingAreaRect = calculateWorkingArea() photoEditor.updateWorkAreaRect(workingAreaRect) + bottomOpaqueBarHeight = preCalculateOpaqueBarHeight() delete_view.addBottomOffset(bottomNavigationBarMargin) delete_slide_view.addBottomOffset(bottomNavigationBarMargin) (bottom_strip_view as StoryFrameSelectorFragment).setBottomOffset(bottomNavigationBarMargin) @@ -552,6 +557,32 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } + private fun preCalculateOpaqueBarHeight(): Int { + val width = resources.displayMetrics.widthPixels + val height = resources.displayMetrics.heightPixels + if (isScreenTallerThan916(width, height)) { + val normalizedSize = normalizeSizeExportTo916(width, height).toSize() + val normalizedHeightDp = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, normalizedSize.height.toFloat(), resources.displayMetrics) + val screenHeightDp = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, height.toFloat(), resources.displayMetrics) + return Math.round(screenHeightDp - normalizedHeightDp) // / 2 + } else { + return 0 + } + } + + private fun setOpaqueBarHeightAndStoryFrameSelectorBackgroundColor() { + photoEditorView.setOpaqueBarHeight(bottomOpaqueBarHeight) + val screenWidth = resources.displayMetrics.widthPixels + val screenHeight = resources.displayMetrics.heightPixels + if (isScreenTallerThan916(screenWidth, screenHeight)) { + (bottom_strip_view as StoryFrameSelectorFragment).setBackgroundColor(R.color.black_opaque_story_frame_selector) + } else { + (bottom_strip_view as StoryFrameSelectorFragment).setBackgroundColor(R.color.black_transp_story_frame_selector) + } + } + override fun onStart() { super.onStart() val selectedFrameIndex = storyViewModel.getSelectedFrameIndex() @@ -1597,10 +1628,13 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun hideStoryFrameSelector() { (bottom_strip_view as StoryFrameSelectorFragment).hide() + photoEditorView.hideOpaqueBar() } private fun showStoryFrameSelector() { + setOpaqueBarHeightAndStoryFrameSelectorBackgroundColor() (bottom_strip_view as StoryFrameSelectorFragment).show() + photoEditorView.showOpaqueBar() } private fun hideEditModeUIControls() { diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt index 22d8d2210..b4a0691f7 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt @@ -31,6 +31,7 @@ interface OnStoryFrameSelectorTappedListener { class StoryFrameSelectorFragment : Fragment() { lateinit var storyViewModel: StoryViewModel private var storyFrameTappedListener: OnStoryFrameSelectorTappedListener? = null + private var backgroundColorResId: Int = R.color.black_transp_story_frame_selector // default background color override fun onCreate(savedInstanceState: Bundle?) { val storyIndex: StoryIndex = getStoryIndexFromIntentOrBundle(savedInstanceState, activity?.intent) @@ -97,6 +98,7 @@ class StoryFrameSelectorFragment : Fragment() { storyViewModel.addButtonClicked.call() } setupItemTouchListener(view) + applyBackgroundColor() view.visibility = View.INVISIBLE return view } @@ -206,6 +208,15 @@ class StoryFrameSelectorFragment : Fragment() { view?.plus_icon?.visibility = View.VISIBLE } + private fun applyBackgroundColor() { + view?.setBackgroundResource(backgroundColorResId) + } + + fun setBackgroundColor(colorResId: Int) { + backgroundColorResId = colorResId + applyBackgroundColor() + } + fun setBottomOffset(offset: Int) { val params = view?.layoutParams as ConstraintLayout.LayoutParams val hasChanged = params.bottomMargin != offset diff --git a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt index 78175618c..fe45e3af2 100644 --- a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt +++ b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt @@ -39,6 +39,10 @@ fun isSizeRatio916(originalWidth: Int, originalHeight: Int): Boolean { return (originalWidth.toFloat() / originalHeight.toFloat()) == TARGET_RATIO_9_16 } +fun isScreenTallerThan916(originalWidth: Int, originalHeight: Int): Boolean { + return (originalWidth.toFloat() / originalHeight.toFloat()) < TARGET_RATIO_9_16 +} + fun normalizeSizeExportTo916(originalWidth: Int, originalHeight: Int): ScreenSize { /* 1. if the screen is 16:9, we're OK diff --git a/stories/src/main/res/values/colors.xml b/stories/src/main/res/values/colors.xml index c6f3939bf..c053ab748 100644 --- a/stories/src/main/res/values/colors.xml +++ b/stories/src/main/res/values/colors.xml @@ -19,6 +19,7 @@ #66000000 #99000000 @color/black_transp_light + @color/black @color/black_transp_light @color/black_transp_light_darker From b893880263e788c370fa8290470a77cef77dd60d Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 3 Mar 2021 14:40:47 -0300 Subject: [PATCH 035/101] removed unused import --- .../java/com/automattic/photoeditor/views/PhotoEditorView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index b44778159..19ecc7a9c 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -18,7 +18,6 @@ import android.widget.ProgressBar import android.widget.RelativeLayout import androidx.annotation.RequiresApi import com.automattic.photoeditor.OnSaveBitmap -import com.automattic.photoeditor.R import com.automattic.photoeditor.R.styleable import com.automattic.photoeditor.views.background.fixed.BackgroundImageView import com.automattic.photoeditor.views.background.video.AutoFitTextureView From 62072d03da138380f4841620a8b7926588b753f7 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 3 Mar 2021 14:44:46 -0300 Subject: [PATCH 036/101] fixed line lenght excess --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 49dd99963..c79ce2a28 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -577,9 +577,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val screenWidth = resources.displayMetrics.widthPixels val screenHeight = resources.displayMetrics.heightPixels if (isScreenTallerThan916(screenWidth, screenHeight)) { - (bottom_strip_view as StoryFrameSelectorFragment).setBackgroundColor(R.color.black_opaque_story_frame_selector) + (bottom_strip_view as StoryFrameSelectorFragment) + .setBackgroundColor(R.color.black_opaque_story_frame_selector) } else { - (bottom_strip_view as StoryFrameSelectorFragment).setBackgroundColor(R.color.black_transp_story_frame_selector) + (bottom_strip_view as StoryFrameSelectorFragment) + .setBackgroundColor(R.color.black_transp_story_frame_selector) } } From 6afe6122ffdc3597c49068b4169ed24cd095a5ad Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 3 Mar 2021 15:33:21 -0300 Subject: [PATCH 037/101] moved the bottomOpaqueBar up from PhotoEditorView to the container in ComposeLoopFrameActivity as it's easier to handle z-index of AddedViews at that level --- .../photoeditor/views/PhotoEditorView.kt | 27 ------------------- .../compose/ComposeLoopFrameActivity.kt | 10 ++++--- .../src/main/res/layout/content_composer.xml | 9 +++++++ 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index 19ecc7a9c..28e4061c9 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -40,7 +40,6 @@ import com.automattic.photoeditor.views.filter.PhotoFilter class PhotoEditorView : RelativeLayout { private lateinit var autoFitTextureView: AutoFitTextureView - private lateinit var bottomOpaqueBar: View private lateinit var backgroundImage: BackgroundImageView private lateinit var brushDrawingView: BrushDrawingView private lateinit var imageFilterView: ImageFilterView @@ -178,16 +177,6 @@ class PhotoEditorView : RelativeLayout { addRule(ALIGN_BOTTOM, imgSrcId) } - // Setup image attributes - bottomOpaqueBar = View(context).apply { - id = bottomOpaqueBarId - setBackgroundColor(Color.parseColor("#000000")) - } - - val bottomOpaqueBarParams = LayoutParams(LayoutParams.MATCH_PARENT, 0).apply { - addRule(ALIGN_PARENT_BOTTOM, TRUE) - } - // Add camera preview addView(autoFitTextureView, cameraParam) @@ -202,9 +191,6 @@ class PhotoEditorView : RelativeLayout { // Add progress view addView(progressBar, progressBarParam) - - // finally add the opaque bar that will be drawn for FIT_SCALE positioning on taller than 9:16 screens - addView(bottomOpaqueBar, bottomOpaqueBarParams) } // added this method as a helper due to the reasons outlined here: @@ -227,18 +213,6 @@ class PhotoEditorView : RelativeLayout { parent.addView(textureView, index, cameraParam) } - fun setOpaqueBarHeight(height: Int) { - bottomOpaqueBar.layoutParams.height = height - } - - fun hideOpaqueBar() { - bottomOpaqueBar.visibility = View.INVISIBLE - } - - fun showOpaqueBar() { - bottomOpaqueBar.visibility = View.VISIBLE - } - internal fun saveFilter(onSaveBitmap: OnSaveBitmap) { // check which background is currently visible: if it's // - imageFilterView: a filter has been applied, process it first @@ -313,7 +287,6 @@ class PhotoEditorView : RelativeLayout { companion object { private val TAG = "PhotoEditorView" - private val bottomOpaqueBarId = 6 private val imgSrcId = 1 private val brushSrcId = 2 private val glFilterId = 3 diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index c79ce2a28..610d7552b 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -573,7 +573,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun setOpaqueBarHeightAndStoryFrameSelectorBackgroundColor() { - photoEditorView.setOpaqueBarHeight(bottomOpaqueBarHeight) + if (bottomOpaqueBarHeight > 0) { + bottom_opaque_bar.layoutParams.height = bottomOpaqueBarHeight + } else { + bottom_opaque_bar.visibility = View.GONE + } val screenWidth = resources.displayMetrics.widthPixels val screenHeight = resources.displayMetrics.heightPixels if (isScreenTallerThan916(screenWidth, screenHeight)) { @@ -1630,13 +1634,13 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun hideStoryFrameSelector() { (bottom_strip_view as StoryFrameSelectorFragment).hide() - photoEditorView.hideOpaqueBar() + bottom_opaque_bar.visibility = View.INVISIBLE } private fun showStoryFrameSelector() { setOpaqueBarHeightAndStoryFrameSelectorBackgroundColor() + bottom_opaque_bar.visibility = View.VISIBLE (bottom_strip_view as StoryFrameSelectorFragment).show() - photoEditorView.showOpaqueBar() } private fun hideEditModeUIControls() { diff --git a/stories/src/main/res/layout/content_composer.xml b/stories/src/main/res/layout/content_composer.xml index d18431d0c..fb8414553 100644 --- a/stories/src/main/res/layout/content_composer.xml +++ b/stories/src/main/res/layout/content_composer.xml @@ -20,6 +20,15 @@ android:animateLayoutChanges="true" android:background="@color/black" /> + + Date: Wed, 3 Mar 2021 15:34:03 -0300 Subject: [PATCH 038/101] removed unused import --- .../java/com/automattic/photoeditor/views/PhotoEditorView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index 28e4061c9..d539dc143 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -3,7 +3,6 @@ package com.automattic.photoeditor.views import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap -import android.graphics.Color import android.graphics.SurfaceTexture import android.os.Build import android.util.AttributeSet From 7d77250904812aeb6beb8b8f029d635ed23fbd81 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 3 Mar 2021 17:39:50 -0300 Subject: [PATCH 039/101] added method onComposerDestroyed so we can avoid trying to load any images in the image listener --- .../com/automattic/photoeditor/views/PhotoEditorView.kt | 7 +++++++ .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 1 + 2 files changed, 8 insertions(+) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index 8b7e4c9d5..185600740 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -79,6 +79,9 @@ class PhotoEditorView : RelativeLayout { val source: ImageView get() = backgroundImage + val sourceBlurredBkg: ImageView + get() = backgroundImageBlurred + val brush: BrushDrawingView get() = brushDrawingView @@ -120,6 +123,10 @@ class PhotoEditorView : RelativeLayout { attachedToWindow = true } + fun onComposerDestroyed() { + attachedToWindow = false + } + @SuppressLint("Recycle") private fun init(attrs: AttributeSet?) { backgroundImageBlurred = AppCompatImageView(context).apply { diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 610d7552b..42781eb19 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -766,6 +766,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } override fun onDestroy() { + photoEditorView.onComposerDestroyed() doUnbindService() EventBus.getDefault().unregister(this) super.onDestroy() From f99c31a78e157aefe81f2fe3d248acb70db5daa9 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 3 Mar 2021 17:41:24 -0300 Subject: [PATCH 040/101] adding blurry image background loading when saving the output file, refactored to return a Pair of futures so these can be cleared after saving --- stories/build.gradle | 2 ++ .../stories/compose/frame/FrameSaveManager.kt | 29 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/stories/build.gradle b/stories/build.gradle index 779778fdd..1c02f8175 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -52,6 +52,8 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.10.0' kapt 'com.github.bumptech.glide:compiler:4.10.0' + implementation 'jp.wasabeef:glide-transformations:4.3.0' + implementation 'org.greenrobot:eventbus:3.1.1' implementation project(path: ':photoeditor') diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index 5993deca5..c1efc29e7 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -24,6 +24,7 @@ import com.wordpress.stories.util.cloneViewSpecs import com.wordpress.stories.util.removeViewFromParent import com.bumptech.glide.Glide import com.bumptech.glide.request.FutureTarget +import com.bumptech.glide.request.RequestOptions import com.wordpress.stories.util.isSizeRatio916 import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.coroutines.CoroutineScope @@ -38,6 +39,7 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.yield import java.io.File import kotlin.coroutines.CoroutineContext +import jp.wasabeef.glide.transformations.BlurTransformation typealias FrameIndex = Int @@ -178,7 +180,7 @@ class FrameSaveManager( frameIndex: FrameIndex ): File { // prepare the ghostview with its background image and the AddedViews on top of it - val futureTarget = preparePhotoEditorViewForSnapshot(context, frame, originalMatrix, ghostPhotoEditorView) + val futureTargetPair = preparePhotoEditorViewForSnapshot(context, frame, originalMatrix, ghostPhotoEditorView) val file = withContext(Dispatchers.IO) { if (normalizeTo916 && !isSizeRatio916(ghostPhotoEditorView.width, ghostPhotoEditorView.height)) { @@ -196,7 +198,8 @@ class FrameSaveManager( } releaseAddedViewsAfterSnapshot(frame) - Glide.with(context).clear(futureTarget) + Glide.with(context).clear(futureTargetPair.first) + Glide.with(context).clear(futureTargetPair.second) return file } @@ -286,11 +289,26 @@ class FrameSaveManager( frame: StoryFrameItem, originalMatrix: Matrix, ghostPhotoEditorView: PhotoEditorView - ): FutureTarget { + ): Pair, FutureTarget> { // prepare background val uri = (frame.source as? UriBackgroundSource)?.contentUri ?: (frame.source as FileBackgroundSource).file + // ----------------------------------- + // first set the background blurred image + val targetBlurredView = ghostPhotoEditorView.sourceBlurredBkg + + // making use of Glide to decode bitmap and get the right orientation automatically + // http://bumptech.github.io/glide/doc/getting-started.html#background-threads + val futureBlurredTarget = Glide.with(context) + .asBitmap() + .load(uri) + .apply(RequestOptions.bitmapTransform(BlurTransformation(25, 3))) + .submit(targetBlurredView.measuredWidth, targetBlurredView.measuredHeight) + targetBlurredView.setImageBitmap(futureBlurredTarget.get()) + + // ----------------------------------- + // now set the actual background image val targetView = ghostPhotoEditorView.source // making use of Glide to decode bitmap and get the right orientation automatically @@ -299,8 +317,7 @@ class FrameSaveManager( .asBitmap() .load(uri) .submit(targetView.measuredWidth, targetView.measuredHeight) - val bitmap = futureTarget.get() - targetView.setImageBitmap(bitmap) + targetView.setImageBitmap(futureTarget.get()) // IMPORTANT: scaleType and setSuppMatrix should only be called _after_ the bitmap is set on the targetView // by means of targetView.setImageBitmap(). Calling this before will have no effect due to PhotoView's checks. @@ -325,7 +342,7 @@ class FrameSaveManager( } } } - return futureTarget + return Pair, FutureTarget>(futureTarget, futureBlurredTarget) } private fun getViewLayoutParams(): LayoutParams { From d28da3a9a69c2f11569093a41d3ba6723d391168 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 3 Mar 2021 17:45:01 -0300 Subject: [PATCH 041/101] fixed lint error --- .../com/wordpress/stories/compose/frame/FrameSaveManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index c1efc29e7..0e114a310 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -289,7 +289,7 @@ class FrameSaveManager( frame: StoryFrameItem, originalMatrix: Matrix, ghostPhotoEditorView: PhotoEditorView - ): Pair, FutureTarget> { + ): Pair, FutureTarget> { // prepare background val uri = (frame.source as? UriBackgroundSource)?.contentUri ?: (frame.source as FileBackgroundSource).file From f4745ab690fd74e5806a562350e2f7d29e2d4d80 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 4 Mar 2021 19:24:45 -0300 Subject: [PATCH 042/101] removed the calculations to get real pixels; these were wrong because all calculations are based on dp so no need to convert to real pixels before passing to layoutParams.height --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 610d7552b..a7e7ed2e2 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -562,11 +562,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val height = resources.displayMetrics.heightPixels if (isScreenTallerThan916(width, height)) { val normalizedSize = normalizeSizeExportTo916(width, height).toSize() - val normalizedHeightDp = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, normalizedSize.height.toFloat(), resources.displayMetrics) - val screenHeightDp = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, height.toFloat(), resources.displayMetrics) - return Math.round(screenHeightDp - normalizedHeightDp) // / 2 + return (height - normalizedSize.height) } else { return 0 } From b1d5d8d6b9b06360cd1c59350177a0ff2c89501b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 4 Mar 2021 19:36:25 -0300 Subject: [PATCH 043/101] removed unused import --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index a7e7ed2e2..7e887fca8 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -25,7 +25,6 @@ import android.os.Vibrator import android.provider.Settings import android.text.TextUtils import android.util.Log -import android.util.TypedValue import android.view.GestureDetector import android.view.Gravity import android.view.MotionEvent From b90e3e1e0da0030d1ee61d892f71092701fbfa55 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 4 Mar 2021 19:38:35 -0300 Subject: [PATCH 044/101] Revert "limit workingArea in calculateWorkingArea() to the same size as exported output" This reverts commit 19066d57106e4fb5d62adb055a6a0ff514e1ebc2. --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 7e887fca8..ee9212afb 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -118,7 +118,6 @@ import com.wordpress.stories.util.getDisplayPixelSize import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle import com.wordpress.stories.util.isScreenTallerThan916 import com.wordpress.stories.util.isVideo -import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.android.synthetic.main.activity_composer.* import kotlinx.android.synthetic.main.content_composer.* import kotlinx.android.synthetic.main.fragment_story_frame_selector.view.* @@ -316,16 +315,14 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val width = photoEditorView.measuredWidth val height = photoEditorView.measuredHeight - val normalizedScreenSize = normalizeSizeExportTo916(width, height).toSize() - val bottomAreaHeight = resources.getDimensionPixelSize(R.dimen.bottom_strip_height) + bottomNavigationBarMargin val topAreaHeight = resources.getDimensionPixelSize(R.dimen.edit_mode_button_size) return Rect( xCoord, yCoord + topAreaHeight, - xCoord + normalizedScreenSize.width, - yCoord + normalizedScreenSize.height - bottomAreaHeight + xCoord + width, + yCoord + height - bottomAreaHeight ) } From f06edcda58d61e4774de59e48a9f35e6aeb6f4b8 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 4 Mar 2021 19:48:01 -0300 Subject: [PATCH 045/101] added missing import --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index ee9212afb..1497951e4 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -118,6 +118,7 @@ import com.wordpress.stories.util.getDisplayPixelSize import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle import com.wordpress.stories.util.isScreenTallerThan916 import com.wordpress.stories.util.isVideo +import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.android.synthetic.main.activity_composer.* import kotlinx.android.synthetic.main.content_composer.* import kotlinx.android.synthetic.main.fragment_story_frame_selector.view.* From 57f0f5527cf388f2eda73df162242908d0b158f8 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 5 Mar 2021 11:00:52 -0300 Subject: [PATCH 046/101] leaving StoryFrameSelector background semitransparent --- app/src/main/res/values/colors.xml | 2 +- .../stories/compose/ComposeLoopFrameActivity.kt | 14 ++------------ stories/src/main/res/layout/content_composer.xml | 2 +- stories/src/main/res/values/colors.xml | 2 +- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index a7d628acf..48e2411c6 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -23,7 +23,7 @@ #99000000 #4D000000 @color/black_transp_light - @color/black + @color/black @color/black_transp_light @color/black_transp_light_darker #4D000000 diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 1497951e4..fa58e03d2 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -121,7 +121,6 @@ import com.wordpress.stories.util.isVideo import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.android.synthetic.main.activity_composer.* import kotlinx.android.synthetic.main.content_composer.* -import kotlinx.android.synthetic.main.fragment_story_frame_selector.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -565,21 +564,12 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } - private fun setOpaqueBarHeightAndStoryFrameSelectorBackgroundColor() { + private fun setOpaqueBarHeight() { if (bottomOpaqueBarHeight > 0) { bottom_opaque_bar.layoutParams.height = bottomOpaqueBarHeight } else { bottom_opaque_bar.visibility = View.GONE } - val screenWidth = resources.displayMetrics.widthPixels - val screenHeight = resources.displayMetrics.heightPixels - if (isScreenTallerThan916(screenWidth, screenHeight)) { - (bottom_strip_view as StoryFrameSelectorFragment) - .setBackgroundColor(R.color.black_opaque_story_frame_selector) - } else { - (bottom_strip_view as StoryFrameSelectorFragment) - .setBackgroundColor(R.color.black_transp_story_frame_selector) - } } override fun onStart() { @@ -1631,7 +1621,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun showStoryFrameSelector() { - setOpaqueBarHeightAndStoryFrameSelectorBackgroundColor() + setOpaqueBarHeight() bottom_opaque_bar.visibility = View.VISIBLE (bottom_strip_view as StoryFrameSelectorFragment).show() } diff --git a/stories/src/main/res/layout/content_composer.xml b/stories/src/main/res/layout/content_composer.xml index fb8414553..10fadc358 100644 --- a/stories/src/main/res/layout/content_composer.xml +++ b/stories/src/main/res/layout/content_composer.xml @@ -26,7 +26,7 @@ android:layout_height="1dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" - android:background="@color/black_opaque_story_frame_selector" + android:background="@color/black_opaque_bar" /> #66000000 #99000000 @color/black_transp_light - @color/black + @color/black @color/black_transp_light @color/black_transp_light_darker From c166db5cdabf5c8ea01d8720af823df34e356a31 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 5 Mar 2021 11:18:13 -0300 Subject: [PATCH 047/101] removed code for changig StoryFrameSelector's background color --- .../compose/story/StoryFrameSelectorFragment.kt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt index b4a0691f7..22d8d2210 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt @@ -31,7 +31,6 @@ interface OnStoryFrameSelectorTappedListener { class StoryFrameSelectorFragment : Fragment() { lateinit var storyViewModel: StoryViewModel private var storyFrameTappedListener: OnStoryFrameSelectorTappedListener? = null - private var backgroundColorResId: Int = R.color.black_transp_story_frame_selector // default background color override fun onCreate(savedInstanceState: Bundle?) { val storyIndex: StoryIndex = getStoryIndexFromIntentOrBundle(savedInstanceState, activity?.intent) @@ -98,7 +97,6 @@ class StoryFrameSelectorFragment : Fragment() { storyViewModel.addButtonClicked.call() } setupItemTouchListener(view) - applyBackgroundColor() view.visibility = View.INVISIBLE return view } @@ -208,15 +206,6 @@ class StoryFrameSelectorFragment : Fragment() { view?.plus_icon?.visibility = View.VISIBLE } - private fun applyBackgroundColor() { - view?.setBackgroundResource(backgroundColorResId) - } - - fun setBackgroundColor(colorResId: Int) { - backgroundColorResId = colorResId - applyBackgroundColor() - } - fun setBottomOffset(offset: Int) { val params = view?.layoutParams as ConstraintLayout.LayoutParams val hasChanged = params.bottomMargin != offset From 8c90acab5c4a5c2f7d27fb61819d7d95b73f3daa Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 5 Mar 2021 18:22:02 -0300 Subject: [PATCH 048/101] implemented Glide loading with FitCenter or CenterCrop transforms according to conditions of aspect ratio matching being met --- .../compose/ComposeLoopFrameActivity.kt | 150 +++++++++++++++--- .../com/wordpress/stories/util/ViewUtils.kt | 15 ++ 2 files changed, 140 insertions(+), 25 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index fa58e03d2..8cd19f5c8 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -25,12 +25,15 @@ import android.os.Vibrator import android.provider.Settings import android.text.TextUtils import android.util.Log +import android.util.Size import android.view.GestureDetector import android.view.Gravity import android.view.MotionEvent import android.view.View import android.view.View.OnClickListener import android.webkit.MimeTypeMap +import android.widget.RelativeLayout +import android.widget.RelativeLayout.LayoutParams import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.Group @@ -67,6 +70,7 @@ import com.automattic.photoeditor.views.added.AddedViewList import com.bumptech.glide.Glide import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation import com.bumptech.glide.load.resource.bitmap.FitCenter import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.RoundedCorners @@ -114,8 +118,12 @@ import com.wordpress.stories.compose.text.TextStyleGroupManager import com.wordpress.stories.util.KEY_STORY_EDIT_MODE import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT import com.wordpress.stories.util.STATE_KEY_CURRENT_STORY_INDEX +import com.wordpress.stories.util.TARGET_RATIO_9_16 +import com.wordpress.stories.util.calculateAspectRatioForDrawable import com.wordpress.stories.util.getDisplayPixelSize +import com.wordpress.stories.util.getSizeRatio import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle +import com.wordpress.stories.util.isAspectRatioSimilarByPercentage import com.wordpress.stories.util.isScreenTallerThan916 import com.wordpress.stories.util.isVideo import com.wordpress.stories.util.normalizeSizeExportTo916 @@ -247,6 +255,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private var genericAnnouncementDialogProvider: GenericAnnouncementDialogProvider? = null private var showGenericAnnouncementDialogWhenReady = false private var useTempCaptureFile = true + private var screenWidth: Int = 1080 //default + private var screenHeight: Int = 1920 //default + private var screenSizeRatio: Float = TARGET_RATIO_9_16 + private lateinit var normalizedSize: Size + private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { @@ -342,6 +355,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec workingAreaRect = calculateWorkingArea() photoEditor.updateWorkAreaRect(workingAreaRect) bottomOpaqueBarHeight = preCalculateOpaqueBarHeight() + screenSizeRatio = getSizeRatio(screenWidth, screenHeight) delete_view.addBottomOffset(bottomNavigationBarMargin) delete_slide_view.addBottomOffset(bottomNavigationBarMargin) (bottom_strip_view as StoryFrameSelectorFragment).setBottomOffset(bottomNavigationBarMargin) @@ -554,11 +568,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun preCalculateOpaqueBarHeight(): Int { - val width = resources.displayMetrics.widthPixels - val height = resources.displayMetrics.heightPixels - if (isScreenTallerThan916(width, height)) { - val normalizedSize = normalizeSizeExportTo916(width, height).toSize() - return (height - normalizedSize.height) + screenWidth = resources.displayMetrics.widthPixels + screenHeight = resources.displayMetrics.heightPixels + if (isScreenTallerThan916(screenWidth, screenHeight)) { + normalizedSize = normalizeSizeExportTo916(screenWidth, screenHeight).toSize() + return (screenHeight - normalizedSize.height) } else { return 0 } @@ -574,11 +588,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec override fun onStart() { super.onStart() - val selectedFrameIndex = storyViewModel.getSelectedFrameIndex() - if (!launchCameraRequestPending && !launchVideoPlayerRequestPending && - selectedFrameIndex < storyViewModel.getCurrentStorySize()) { - updateBackgroundSurfaceUIWithStoryFrame(selectedFrameIndex) - } +// val selectedFrameIndex = storyViewModel.getSelectedFrameIndex() +// if (!launchCameraRequestPending && !launchVideoPlayerRequestPending && +// selectedFrameIndex < storyViewModel.getCurrentStorySize()) { +// updateBackgroundSurfaceUIWithStoryFrame(selectedFrameIndex) +// } // upon loading an existing Story, show the generic announcement dialog if present if (showGenericAnnouncementDialogWhenReady) { showGenericAnnouncementDialogWhenReady = false @@ -1953,18 +1967,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (newSelectedFrame.frameItemType is VIDEO) { showPlayVideoWithSurfaceSafeguard(source) } else { - val model = (source as? FileBackgroundSource)?.file ?: (source as UriBackgroundSource).contentUri - Glide.with(this@ComposeLoopFrameActivity) - .load(model) - .transform(FitCenter()) - .listener(provideGlideRequestListenerWithHandler { - setBackgroundViewInfoOnPhotoView( - newSelectedFrame, - photoEditor.composedCanvas.source as PhotoView - ) - }) - .into(photoEditorView.source) - + loadImageWithGlide(newSelectedFrame) showStaticBackground() } @@ -2007,7 +2010,100 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } - private fun provideGlideRequestListenerWithHandler(setupPhotoViewMatrix: Runnable): RequestListener { + private fun loadImageWithGlide(frame: StoryFrameItem, useFitCenter: Boolean = false) { + val model = (frame.source as? FileBackgroundSource)?.file ?: + (frame.source as UriBackgroundSource).contentUri + // 0. attempt center-crop to pre-load the image and we can retrieve the intrinsic width/height. Also, before + // we know anything about the image Center-crop is our best bet, as it should be the widest use case. + // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top + // (no parts would actually be cropped, given the matching the aspect ratio it should fit) + // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) + // 3. else, load with fit-center + + val transformToUse: BitmapTransformation = if (useFitCenter) { + FitCenter() + } else { + CenterCrop() + } + + // reset opaque bar to GONE + bottom_opaque_bar.visibility = View.GONE + // photoEditorView.source.layoutParams.height = 1920 + //(photoEditorView.source.layoutParams as LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) + // addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE) + // center in parent by default + (photoEditorView.source.layoutParams as LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE) + Glide.with(this@ComposeLoopFrameActivity) + .load(model) + //.transform(FitCenter()) + // .transform(CenterCrop()) + .transform(transformToUse) + .listener(provideGlideRequestListenerWithHandler( + object : ResourceLoader { + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + // early exit if fit-centering + if (useFitCenter) { + return false + } + + // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top + // (no parts would actually be cropped, given the matching aspect ratio it should fit) + val drawableAspectRatio = calculateAspectRatioForDrawable(resource!!) + if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.001f)) { + (photoEditorView.source.layoutParams as LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) + bottom_opaque_bar.visibility = View.GONE + } else { + // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) + if (isScreenTallerThan916(screenWidth, screenHeight)) { + (photoEditorView.source.layoutParams as LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) + bottom_opaque_bar.visibility = View.VISIBLE + photoEditorView.source.layoutParams.height = normalizedSize.height + } else { + // 3. else, load with fit-center + Handler().post { + // we need to call Glide's .into() from the UI thread, so + // this recursive call needs to be made within a Handler() + loadImageWithGlide(frame, true) + } + + // return true tells Glide we're handling things to avoid the drawable being set + // at this time + return true + } + } + // else, return false to make Glide set the bitmap on the target + return false + } + }, object: Runnable { + override fun run() { + setBackgroundViewInfoOnPhotoView( + frame, + photoEditor.composedCanvas.source as PhotoView + ) + } + })) + .into(photoEditorView.source) + } + + interface ResourceLoader { + fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean + } + + private fun provideGlideRequestListenerWithHandler(doBefore: ResourceLoader, setupPhotoViewMatrix: Runnable): RequestListener { return object : RequestListener { override fun onLoadFailed( e: GlideException?, @@ -2031,9 +2127,13 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // has been set on the PhotoView, otherwise the matrix is not applied // see // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 - Handler().post(setupPhotoViewMatrix) + val result = doBefore.onResourceReady(resource, model, target, dataSource, isFirstResource) + if (!result) { + // if we're all good, let's setup the ViewMatrix + Handler().post(setupPhotoViewMatrix) + } // return false to let Glide proceed and set the drawable - return false + return result } } } diff --git a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt index fe45e3af2..b3359616c 100644 --- a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt +++ b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt @@ -1,5 +1,6 @@ package com.wordpress.stories.util +import android.graphics.drawable.Drawable import android.util.Size import android.view.View import android.view.ViewGroup @@ -69,3 +70,17 @@ fun normalizeSizeExportTo916(originalWidth: Int, originalHeight: Int): ScreenSiz } } } + +fun calculateAspectRatioForDrawable(drawable: Drawable): Float { + val width = drawable.intrinsicWidth + val height = drawable.intrinsicHeight + return width.toFloat() / height.toFloat() +} + +fun isAspectRatioSimilarByPercentage(aspectRatio1: Float, aspectRatio2: Float, percentage: Float): Boolean { + return (Math.abs(aspectRatio1 - aspectRatio2) < percentage) +} + +fun getSizeRatio(originalWidth: Int, originalHeight: Int): Float { + return (originalWidth.toFloat() / originalHeight.toFloat()) +} \ No newline at end of file From 2c071049537950bd6d8f180fea533a5a1a80e48b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 8 Mar 2021 12:32:47 -0300 Subject: [PATCH 049/101] changed wording in comment --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 8cd19f5c8..d7d834c56 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2014,7 +2014,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val model = (frame.source as? FileBackgroundSource)?.file ?: (frame.source as UriBackgroundSource).contentUri // 0. attempt center-crop to pre-load the image and we can retrieve the intrinsic width/height. Also, before - // we know anything about the image Center-crop is our best bet, as it should be the widest use case. + // we know anything about the image Center-crop is our best bet, as it should be the most common use case. // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top // (no parts would actually be cropped, given the matching the aspect ratio it should fit) // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) From ce82e4535befd837b7ca8b02763f587c46a3e9e0 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 8 Mar 2021 17:23:34 -0300 Subject: [PATCH 050/101] split method into two parts to avoid recursion, using coroutines to first prepare the bitmap then load it --- .../compose/ComposeLoopFrameActivity.kt | 173 +++++++++--------- .../com/wordpress/stories/util/ViewUtils.kt | 15 ++ 2 files changed, 104 insertions(+), 84 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index d7d834c56..b52d78447 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -32,6 +32,8 @@ import android.view.MotionEvent import android.view.View import android.view.View.OnClickListener import android.webkit.MimeTypeMap +import android.widget.ImageView.ScaleType.CENTER_CROP +import android.widget.ImageView.ScaleType.FIT_CENTER import android.widget.RelativeLayout import android.widget.RelativeLayout.LayoutParams import android.widget.Toast @@ -120,11 +122,13 @@ import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT import com.wordpress.stories.util.STATE_KEY_CURRENT_STORY_INDEX import com.wordpress.stories.util.TARGET_RATIO_9_16 import com.wordpress.stories.util.calculateAspectRatioForDrawable +import com.wordpress.stories.util.calculateAspectRatioForBitmap import com.wordpress.stories.util.getDisplayPixelSize import com.wordpress.stories.util.getSizeRatio import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle import com.wordpress.stories.util.isAspectRatioSimilarByPercentage import com.wordpress.stories.util.isScreenTallerThan916 +import com.wordpress.stories.util.isWidthMultiple import com.wordpress.stories.util.isVideo import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.android.synthetic.main.activity_composer.* @@ -132,6 +136,7 @@ import kotlinx.android.synthetic.main.content_composer.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -258,6 +263,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private var screenWidth: Int = 1080 //default private var screenHeight: Int = 1920 //default private var screenSizeRatio: Float = TARGET_RATIO_9_16 + private var originalCanvasHeight = screenHeight private lateinit var normalizedSize: Size @@ -570,6 +576,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun preCalculateOpaqueBarHeight(): Int { screenWidth = resources.displayMetrics.widthPixels screenHeight = resources.displayMetrics.heightPixels + originalCanvasHeight = photoEditorView.source.layoutParams.height if (isScreenTallerThan916(screenWidth, screenHeight)) { normalizedSize = normalizeSizeExportTo916(screenWidth, screenHeight).toSize() return (screenHeight - normalizedSize.height) @@ -1967,7 +1974,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (newSelectedFrame.frameItemType is VIDEO) { showPlayVideoWithSurfaceSafeguard(source) } else { - loadImageWithGlide(newSelectedFrame) + loadImageWithGlideToPrepare(newSelectedFrame) showStaticBackground() } @@ -2010,92 +2017,87 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } - private fun loadImageWithGlide(frame: StoryFrameItem, useFitCenter: Boolean = false) { + private fun loadImageWithGlideToPrepare(frame: StoryFrameItem) { val model = (frame.source as? FileBackgroundSource)?.file ?: - (frame.source as UriBackgroundSource).contentUri - // 0. attempt center-crop to pre-load the image and we can retrieve the intrinsic width/height. Also, before - // we know anything about the image Center-crop is our best bet, as it should be the most common use case. - // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top - // (no parts would actually be cropped, given the matching the aspect ratio it should fit) - // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) - // 3. else, load with fit-center - - val transformToUse: BitmapTransformation = if (useFitCenter) { - FitCenter() - } else { - CenterCrop() - } - - // reset opaque bar to GONE - bottom_opaque_bar.visibility = View.GONE - // photoEditorView.source.layoutParams.height = 1920 - //(photoEditorView.source.layoutParams as LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) - // addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE) - // center in parent by default - (photoEditorView.source.layoutParams as LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE) - Glide.with(this@ComposeLoopFrameActivity) - .load(model) - //.transform(FitCenter()) - // .transform(CenterCrop()) - .transform(transformToUse) - .listener(provideGlideRequestListenerWithHandler( - object : ResourceLoader { - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - // early exit if fit-centering - if (useFitCenter) { - return false - } + (frame.source as UriBackgroundSource).contentUri + + CoroutineScope(Dispatchers.IO).launch { + val futureTarget = Glide.with(this@ComposeLoopFrameActivity) + .asBitmap() + .load(model) + //.submit(screenWidth, screenHeight) + .submit() + val bitmap = futureTarget.get() + + withContext(Dispatchers.Main) { + // targetView.setImageBitmap(bitmap) + // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top + // (no parts would actually be cropped, given the matching aspect ratio it should fit) + val drawableAspectRatio = calculateAspectRatioForBitmap(bitmap) + if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.001f)) { + (photoEditorView.source.layoutParams as LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) + bottom_opaque_bar.visibility = View.GONE + // photoEditorView.source.layoutParams.height = originalCanvasHeight + photoEditorView.source.scaleType = CENTER_CROP + loadImageWithGlideToDraw(frame, false) + } else { + // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) + if (isScreenTallerThan916(screenWidth, screenHeight) + && (isWidthMultiple(screenWidth, bitmap.width) || + isWidthMultiple(bitmap.width, screenWidth)) + ) { + (photoEditorView.source.layoutParams as LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) + bottom_opaque_bar.visibility = View.VISIBLE + // photoEditorView.source.layoutParams.height = normalizedSize.height + photoEditorView.source.scaleType = CENTER_CROP + // photoEditorView.source.scaleType = FIT_CENTER + loadImageWithGlideToDraw(frame, false) + } else { + // 3. else, load with fit-center + (photoEditorView.source.layoutParams as LayoutParams) + .addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE) + bottom_opaque_bar.visibility = View.GONE + // photoEditorView.source.layoutParams.height = originalCanvasHeight + photoEditorView.source.scaleType = FIT_CENTER + // we need to call Glide's .into() from the UI thread, so + // this recursive call needs to be made within a Handler() + loadImageWithGlideToDraw(frame, true) + } + } + } + Glide.with(this@ComposeLoopFrameActivity).clear(futureTarget) + } + } - // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top - // (no parts would actually be cropped, given the matching aspect ratio it should fit) - val drawableAspectRatio = calculateAspectRatioForDrawable(resource!!) - if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.001f)) { - (photoEditorView.source.layoutParams as LayoutParams) - .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) - bottom_opaque_bar.visibility = View.GONE - } else { - // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) - if (isScreenTallerThan916(screenWidth, screenHeight)) { - (photoEditorView.source.layoutParams as LayoutParams) - .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) - bottom_opaque_bar.visibility = View.VISIBLE - photoEditorView.source.layoutParams.height = normalizedSize.height - } else { - // 3. else, load with fit-center - Handler().post { - // we need to call Glide's .into() from the UI thread, so - // this recursive call needs to be made within a Handler() - loadImageWithGlide(frame, true) - } - - // return true tells Glide we're handling things to avoid the drawable being set - // at this time - return true - } - } - // else, return false to make Glide set the bitmap on the target - return false - } - }, object: Runnable { - override fun run() { + private suspend fun loadImageWithGlideToDraw(frame: StoryFrameItem, useFitCenter: Boolean = false) { + withContext(Dispatchers.Main) { + val model = (frame.source as? FileBackgroundSource)?.file ?: + (frame.source as UriBackgroundSource).contentUri + + val transformToUse: BitmapTransformation = if (useFitCenter) { + FitCenter() + } else { + CenterCrop() + } + + Glide.with(this@ComposeLoopFrameActivity) + .load(model) + .transform(transformToUse) + .listener(provideGlideRequestListenerWithHandler(setupPhotoViewMatrix = { setBackgroundViewInfoOnPhotoView( frame, photoEditor.composedCanvas.source as PhotoView ) - } - })) - .into(photoEditorView.source) + })) + .into(photoEditorView.source) + } } interface ResourceLoader { fun onResourceReady( - resource: Drawable?, + resource: Drawable, model: Any?, target: Target?, dataSource: DataSource?, @@ -2103,7 +2105,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec ): Boolean } - private fun provideGlideRequestListenerWithHandler(doBefore: ResourceLoader, setupPhotoViewMatrix: Runnable): RequestListener { + private fun provideGlideRequestListenerWithHandler(doBefore: ResourceLoader? = null, setupPhotoViewMatrix: Runnable): RequestListener { return object : RequestListener { override fun onLoadFailed( e: GlideException?, @@ -2127,13 +2129,16 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // has been set on the PhotoView, otherwise the matrix is not applied // see // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 - val result = doBefore.onResourceReady(resource, model, target, dataSource, isFirstResource) - if (!result) { - // if we're all good, let's setup the ViewMatrix - Handler().post(setupPhotoViewMatrix) - } + doBefore?.let { + val result = doBefore.onResourceReady(requireNotNull(resource), model, target, dataSource, isFirstResource) + if (!result) { + // if we're all good, let's setup the ViewMatrix + Handler().post(setupPhotoViewMatrix) + } + return result + } ?: Handler().post(setupPhotoViewMatrix) // return false to let Glide proceed and set the drawable - return result + return false } } } diff --git a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt index b3359616c..47736a72a 100644 --- a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt +++ b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt @@ -1,5 +1,6 @@ package com.wordpress.stories.util +import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.util.Size import android.view.View @@ -77,10 +78,24 @@ fun calculateAspectRatioForDrawable(drawable: Drawable): Float { return width.toFloat() / height.toFloat() } +fun calculateAspectRatioForBitmap(bitmap: Bitmap): Float { + val width = bitmap.width + val height = bitmap.height + return width.toFloat() / height.toFloat() +} + fun isAspectRatioSimilarByPercentage(aspectRatio1: Float, aspectRatio2: Float, percentage: Float): Boolean { return (Math.abs(aspectRatio1 - aspectRatio2) < percentage) } +fun isWidthMultiple(width1: Int, width2: Int): Boolean { + return isMultipleOf(width1, width2) || isMultipleOf(width2, width1) +} + +private fun isMultipleOf(num1: Int, num2: Int): Boolean { + return (num1 % num2 == 0) +} + fun getSizeRatio(originalWidth: Int, originalHeight: Int): Float { return (originalWidth.toFloat() / originalHeight.toFloat()) } \ No newline at end of file From 02f98909d60cbeedc92606c3e5762377c9908aa7 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 8 Mar 2021 17:30:28 -0300 Subject: [PATCH 051/101] passing bitmap on glide second pass so it doesn't need be fetched and processed again --- .../stories/compose/ComposeLoopFrameActivity.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index b52d78447..d69375ee1 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -9,6 +9,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection +import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.graphics.Rect import android.graphics.drawable.ColorDrawable @@ -2040,7 +2041,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec bottom_opaque_bar.visibility = View.GONE // photoEditorView.source.layoutParams.height = originalCanvasHeight photoEditorView.source.scaleType = CENTER_CROP - loadImageWithGlideToDraw(frame, false) + loadImageWithGlideToDraw(bitmap, frame, false) } else { // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) if (isScreenTallerThan916(screenWidth, screenHeight) @@ -2053,7 +2054,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // photoEditorView.source.layoutParams.height = normalizedSize.height photoEditorView.source.scaleType = CENTER_CROP // photoEditorView.source.scaleType = FIT_CENTER - loadImageWithGlideToDraw(frame, false) + loadImageWithGlideToDraw(bitmap, frame,false) } else { // 3. else, load with fit-center (photoEditorView.source.layoutParams as LayoutParams) @@ -2063,7 +2064,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec photoEditorView.source.scaleType = FIT_CENTER // we need to call Glide's .into() from the UI thread, so // this recursive call needs to be made within a Handler() - loadImageWithGlideToDraw(frame, true) + loadImageWithGlideToDraw(bitmap, frame,true) } } } @@ -2071,11 +2072,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } - private suspend fun loadImageWithGlideToDraw(frame: StoryFrameItem, useFitCenter: Boolean = false) { + private suspend fun loadImageWithGlideToDraw(bitmap: Bitmap, frame: StoryFrameItem, useFitCenter: Boolean = false) { withContext(Dispatchers.Main) { - val model = (frame.source as? FileBackgroundSource)?.file ?: - (frame.source as UriBackgroundSource).contentUri - val transformToUse: BitmapTransformation = if (useFitCenter) { FitCenter() } else { @@ -2083,7 +2081,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } Glide.with(this@ComposeLoopFrameActivity) - .load(model) + .load(bitmap) .transform(transformToUse) .listener(provideGlideRequestListenerWithHandler(setupPhotoViewMatrix = { setBackgroundViewInfoOnPhotoView( From 3da98442c0d01da0b0ead3862640fcb301d4f644 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 9 Mar 2021 14:33:11 -0300 Subject: [PATCH 052/101] using Drawable instead of bitmap, will be easier to use in the future when using animated sources --- .../compose/ComposeLoopFrameActivity.kt | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index d69375ee1..9f1c1e369 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2024,29 +2024,28 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec CoroutineScope(Dispatchers.IO).launch { val futureTarget = Glide.with(this@ComposeLoopFrameActivity) - .asBitmap() + .asDrawable() .load(model) - //.submit(screenWidth, screenHeight) .submit() - val bitmap = futureTarget.get() + val drawable = futureTarget.get() withContext(Dispatchers.Main) { - // targetView.setImageBitmap(bitmap) // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top // (no parts would actually be cropped, given the matching aspect ratio it should fit) - val drawableAspectRatio = calculateAspectRatioForBitmap(bitmap) + // val drawableAspectRatio = calculateAspectRatioForBitmap(bitmap) + val drawableAspectRatio = calculateAspectRatioForDrawable(drawable) if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.001f)) { (photoEditorView.source.layoutParams as LayoutParams) .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) bottom_opaque_bar.visibility = View.GONE // photoEditorView.source.layoutParams.height = originalCanvasHeight photoEditorView.source.scaleType = CENTER_CROP - loadImageWithGlideToDraw(bitmap, frame, false) + loadImageWithGlideToDraw(drawable, frame, false) } else { // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) if (isScreenTallerThan916(screenWidth, screenHeight) - && (isWidthMultiple(screenWidth, bitmap.width) || - isWidthMultiple(bitmap.width, screenWidth)) + && (isWidthMultiple(screenWidth, drawable.intrinsicWidth) || + isWidthMultiple(drawable.intrinsicWidth, screenWidth)) ) { (photoEditorView.source.layoutParams as LayoutParams) .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) @@ -2054,7 +2053,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // photoEditorView.source.layoutParams.height = normalizedSize.height photoEditorView.source.scaleType = CENTER_CROP // photoEditorView.source.scaleType = FIT_CENTER - loadImageWithGlideToDraw(bitmap, frame,false) + loadImageWithGlideToDraw(drawable, frame, false) } else { // 3. else, load with fit-center (photoEditorView.source.layoutParams as LayoutParams) @@ -2062,9 +2061,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec bottom_opaque_bar.visibility = View.GONE // photoEditorView.source.layoutParams.height = originalCanvasHeight photoEditorView.source.scaleType = FIT_CENTER - // we need to call Glide's .into() from the UI thread, so - // this recursive call needs to be made within a Handler() - loadImageWithGlideToDraw(bitmap, frame,true) + loadImageWithGlideToDraw(drawable, frame, true) } } } @@ -2072,7 +2069,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } - private suspend fun loadImageWithGlideToDraw(bitmap: Bitmap, frame: StoryFrameItem, useFitCenter: Boolean = false) { + private suspend fun loadImageWithGlideToDraw( + drawable: Drawable, + frame: StoryFrameItem, + useFitCenter: Boolean = false + ) { withContext(Dispatchers.Main) { val transformToUse: BitmapTransformation = if (useFitCenter) { FitCenter() @@ -2081,7 +2082,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } Glide.with(this@ComposeLoopFrameActivity) - .load(bitmap) + .load(drawable) .transform(transformToUse) .listener(provideGlideRequestListenerWithHandler(setupPhotoViewMatrix = { setBackgroundViewInfoOnPhotoView( From 3ec01b382b3ffa122590d2c00d701dc5659ee937 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 9 Mar 2021 15:22:14 -0300 Subject: [PATCH 053/101] removed ResourceLoader interface, replaced with simpler ImageLoadedInterface --- .../compose/ComposeLoopFrameActivity.kt | 65 +++++++++---------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 9f1c1e369..c320ceaf8 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2029,6 +2029,26 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec .submit() val drawable = futureTarget.get() + val doAfterUse = object : ImageLoadedInterface { + override fun doAfter() { + // here setup the PhotoView support matrix + // we use a Handler because we need to set the support matrix only once the drawable + // has been set on the PhotoView, otherwise the matrix is not applied + // see + // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 + // if we're all good, doAfter() will be callled on Glide's `onResourceReady`, so + // let's setup the ViewMatrix + Handler().post{ + setBackgroundViewInfoOnPhotoView( + frame, + photoEditor.composedCanvas.source as PhotoView + ) + // finally, clean target so resources can be freed up + Glide.with(this@ComposeLoopFrameActivity).clear(futureTarget) + } + } + } + withContext(Dispatchers.Main) { // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top // (no parts would actually be cropped, given the matching aspect ratio it should fit) @@ -2040,7 +2060,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec bottom_opaque_bar.visibility = View.GONE // photoEditorView.source.layoutParams.height = originalCanvasHeight photoEditorView.source.scaleType = CENTER_CROP - loadImageWithGlideToDraw(drawable, frame, false) + loadImageWithGlideToDraw(drawable,false, doAfterUse) } else { // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) if (isScreenTallerThan916(screenWidth, screenHeight) @@ -2053,7 +2073,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // photoEditorView.source.layoutParams.height = normalizedSize.height photoEditorView.source.scaleType = CENTER_CROP // photoEditorView.source.scaleType = FIT_CENTER - loadImageWithGlideToDraw(drawable, frame, false) + loadImageWithGlideToDraw(drawable, false, doAfterUse) } else { // 3. else, load with fit-center (photoEditorView.source.layoutParams as LayoutParams) @@ -2061,18 +2081,17 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec bottom_opaque_bar.visibility = View.GONE // photoEditorView.source.layoutParams.height = originalCanvasHeight photoEditorView.source.scaleType = FIT_CENTER - loadImageWithGlideToDraw(drawable, frame, true) + loadImageWithGlideToDraw(drawable, true, doAfterUse) } } } - Glide.with(this@ComposeLoopFrameActivity).clear(futureTarget) } } private suspend fun loadImageWithGlideToDraw( drawable: Drawable, - frame: StoryFrameItem, - useFitCenter: Boolean = false + useFitCenter: Boolean = false, + doAfterUse: ImageLoadedInterface ) { withContext(Dispatchers.Main) { val transformToUse: BitmapTransformation = if (useFitCenter) { @@ -2084,27 +2103,17 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec Glide.with(this@ComposeLoopFrameActivity) .load(drawable) .transform(transformToUse) - .listener(provideGlideRequestListenerWithHandler(setupPhotoViewMatrix = { - setBackgroundViewInfoOnPhotoView( - frame, - photoEditor.composedCanvas.source as PhotoView - ) - })) + .listener(provideGlideRequestListener(doAfterUse)) .into(photoEditorView.source) + } } - interface ResourceLoader { - fun onResourceReady( - resource: Drawable, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean + interface ImageLoadedInterface { + fun doAfter() } - private fun provideGlideRequestListenerWithHandler(doBefore: ResourceLoader? = null, setupPhotoViewMatrix: Runnable): RequestListener { + private fun provideGlideRequestListener(callback: ImageLoadedInterface): RequestListener { return object : RequestListener { override fun onLoadFailed( e: GlideException?, @@ -2123,19 +2132,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec dataSource: DataSource?, isFirstResource: Boolean ): Boolean { - // here setup the PhotoView support matrix - // we use a handler because we need to set the support matrix only once the drawable - // has been set on the PhotoView, otherwise the matrix is not applied - // see - // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 - doBefore?.let { - val result = doBefore.onResourceReady(requireNotNull(resource), model, target, dataSource, isFirstResource) - if (!result) { - // if we're all good, let's setup the ViewMatrix - Handler().post(setupPhotoViewMatrix) - } - return result - } ?: Handler().post(setupPhotoViewMatrix) + callback.doAfter() // return false to let Glide proceed and set the drawable return false } From a654693b717e55989ef5f4518995ad6ffaff1f96 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 9 Mar 2021 21:48:33 -0300 Subject: [PATCH 054/101] several improvements: only loading futurTarget to as max as the screen size since that's the capped output, and using Glide's override() to load for the calculated size. Also using FIT_START for taller than 9:16 devices which makes it occupy the top part of the screen and only crop the bottom --- .../compose/ComposeLoopFrameActivity.kt | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index c320ceaf8..6a2c1eab5 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -35,6 +35,7 @@ import android.view.View.OnClickListener import android.webkit.MimeTypeMap import android.widget.ImageView.ScaleType.CENTER_CROP import android.widget.ImageView.ScaleType.FIT_CENTER +import android.widget.ImageView.ScaleType.FIT_START import android.widget.RelativeLayout import android.widget.RelativeLayout.LayoutParams import android.widget.Toast @@ -2026,7 +2027,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val futureTarget = Glide.with(this@ComposeLoopFrameActivity) .asDrawable() .load(model) - .submit() + .fitCenter() // we use fitCenter at first (instead of cropping) so we don't lose any information + .submit(screenWidth, screenHeight) // we're not going to export images greater than the screen size val drawable = futureTarget.get() val doAfterUse = object : ImageLoadedInterface { @@ -2052,36 +2054,25 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec withContext(Dispatchers.Main) { // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top // (no parts would actually be cropped, given the matching aspect ratio it should fit) - // val drawableAspectRatio = calculateAspectRatioForBitmap(bitmap) val drawableAspectRatio = calculateAspectRatioForDrawable(drawable) - if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.001f)) { - (photoEditorView.source.layoutParams as LayoutParams) - .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) + if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.01f)) { bottom_opaque_bar.visibility = View.GONE - // photoEditorView.source.layoutParams.height = originalCanvasHeight photoEditorView.source.scaleType = CENTER_CROP - loadImageWithGlideToDraw(drawable,false, doAfterUse) + loadImageWithGlideToDraw(drawable, CenterCrop(), screenWidth, originalCanvasHeight, doAfterUse) } else { - // 2. if the device is taller than 9:16, just crop the bottom (showing the opaque bar) - if (isScreenTallerThan916(screenWidth, screenHeight) - && (isWidthMultiple(screenWidth, drawable.intrinsicWidth) || - isWidthMultiple(drawable.intrinsicWidth, screenWidth)) + // 2. if the device is taller than 9:16, and image is portrait + // just crop the bottom (showing the opaque bar) + if (isScreenTallerThan916(screenWidth, screenHeight) && + (drawable.intrinsicHeight > drawable.intrinsicWidth) ) { - (photoEditorView.source.layoutParams as LayoutParams) - .addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE) bottom_opaque_bar.visibility = View.VISIBLE - // photoEditorView.source.layoutParams.height = normalizedSize.height - photoEditorView.source.scaleType = CENTER_CROP - // photoEditorView.source.scaleType = FIT_CENTER - loadImageWithGlideToDraw(drawable, false, doAfterUse) + photoEditorView.source.scaleType = FIT_START + loadImageWithGlideToDraw(drawable, null, normalizedSize.width, normalizedSize.height, doAfterUse) } else { // 3. else, load with fit-center - (photoEditorView.source.layoutParams as LayoutParams) - .addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE) - bottom_opaque_bar.visibility = View.GONE - // photoEditorView.source.layoutParams.height = originalCanvasHeight + bottom_opaque_bar.visibility = View.VISIBLE photoEditorView.source.scaleType = FIT_CENTER - loadImageWithGlideToDraw(drawable, true, doAfterUse) + loadImageWithGlideToDraw(drawable, FitCenter(), screenWidth, originalCanvasHeight, doAfterUse) } } } @@ -2090,22 +2081,25 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private suspend fun loadImageWithGlideToDraw( drawable: Drawable, - useFitCenter: Boolean = false, + transformToUse: BitmapTransformation? = null, + overrideWidth: Int, + overrideHeight: Int, doAfterUse: ImageLoadedInterface ) { withContext(Dispatchers.Main) { - val transformToUse: BitmapTransformation = if (useFitCenter) { - FitCenter() - } else { - CenterCrop() - } + transformToUse?.let { + Glide.with(this@ComposeLoopFrameActivity) + .load(drawable) + .transform(it) + .listener(provideGlideRequestListener(doAfterUse)) + .override(overrideWidth, overrideHeight) + .into(photoEditorView.source) - Glide.with(this@ComposeLoopFrameActivity) + } ?: Glide.with(this@ComposeLoopFrameActivity) .load(drawable) - .transform(transformToUse) .listener(provideGlideRequestListener(doAfterUse)) + .override(overrideWidth, overrideHeight) .into(photoEditorView.source) - } } From 92ccda0adc8efc8c246810577dc31938e22ef4d4 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 9 Mar 2021 21:49:21 -0300 Subject: [PATCH 055/101] removed unused functions --- .../java/com/wordpress/stories/util/ViewUtils.kt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt index 47736a72a..2f3b3c8f3 100644 --- a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt +++ b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt @@ -1,6 +1,5 @@ package com.wordpress.stories.util -import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.util.Size import android.view.View @@ -78,24 +77,9 @@ fun calculateAspectRatioForDrawable(drawable: Drawable): Float { return width.toFloat() / height.toFloat() } -fun calculateAspectRatioForBitmap(bitmap: Bitmap): Float { - val width = bitmap.width - val height = bitmap.height - return width.toFloat() / height.toFloat() -} - fun isAspectRatioSimilarByPercentage(aspectRatio1: Float, aspectRatio2: Float, percentage: Float): Boolean { return (Math.abs(aspectRatio1 - aspectRatio2) < percentage) } - -fun isWidthMultiple(width1: Int, width2: Int): Boolean { - return isMultipleOf(width1, width2) || isMultipleOf(width2, width1) -} - -private fun isMultipleOf(num1: Int, num2: Int): Boolean { - return (num1 % num2 == 0) -} - fun getSizeRatio(originalWidth: Int, originalHeight: Int): Float { return (originalWidth.toFloat() / originalHeight.toFloat()) } \ No newline at end of file From 089917adab05bb2b585da922885a781b4a6b1f10 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 9 Mar 2021 21:52:59 -0300 Subject: [PATCH 056/101] removed commented code --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 6a2c1eab5..cef566de6 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -597,11 +597,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec override fun onStart() { super.onStart() -// val selectedFrameIndex = storyViewModel.getSelectedFrameIndex() -// if (!launchCameraRequestPending && !launchVideoPlayerRequestPending && -// selectedFrameIndex < storyViewModel.getCurrentStorySize()) { -// updateBackgroundSurfaceUIWithStoryFrame(selectedFrameIndex) -// } // upon loading an existing Story, show the generic announcement dialog if present if (showGenericAnnouncementDialogWhenReady) { showGenericAnnouncementDialogWhenReady = false From 6b440d51756832ae541146a86b301189ce969258 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 11:01:03 -0300 Subject: [PATCH 057/101] passing scaleType in BackgroundViewInfo, also now calculating screenHeight from photoEditorView.source.measuredHeight to get the actual real screen height --- .../compose/ComposeLoopFrameActivity.kt | 27 +++++++++---------- .../stories/compose/story/StoryFrameItem.kt | 4 ++- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index cef566de6..aa301a49e 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -9,7 +9,6 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection -import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.graphics.Rect import android.graphics.drawable.ColorDrawable @@ -36,8 +35,6 @@ import android.webkit.MimeTypeMap import android.widget.ImageView.ScaleType.CENTER_CROP import android.widget.ImageView.ScaleType.FIT_CENTER import android.widget.ImageView.ScaleType.FIT_START -import android.widget.RelativeLayout -import android.widget.RelativeLayout.LayoutParams import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.Group @@ -124,13 +121,11 @@ import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT import com.wordpress.stories.util.STATE_KEY_CURRENT_STORY_INDEX import com.wordpress.stories.util.TARGET_RATIO_9_16 import com.wordpress.stories.util.calculateAspectRatioForDrawable -import com.wordpress.stories.util.calculateAspectRatioForBitmap import com.wordpress.stories.util.getDisplayPixelSize import com.wordpress.stories.util.getSizeRatio import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle import com.wordpress.stories.util.isAspectRatioSimilarByPercentage import com.wordpress.stories.util.isScreenTallerThan916 -import com.wordpress.stories.util.isWidthMultiple import com.wordpress.stories.util.isVideo import com.wordpress.stories.util.normalizeSizeExportTo916 import kotlinx.android.synthetic.main.activity_composer.* @@ -262,10 +257,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private var genericAnnouncementDialogProvider: GenericAnnouncementDialogProvider? = null private var showGenericAnnouncementDialogWhenReady = false private var useTempCaptureFile = true - private var screenWidth: Int = 1080 //default - private var screenHeight: Int = 1920 //default + private var screenWidth: Int = 1080 // default + private var screenHeight: Int = 1920 // default private var screenSizeRatio: Float = TARGET_RATIO_9_16 - private var originalCanvasHeight = screenHeight private lateinit var normalizedSize: Size @@ -576,9 +570,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun preCalculateOpaqueBarHeight(): Int { - screenWidth = resources.displayMetrics.widthPixels - screenHeight = resources.displayMetrics.heightPixels - originalCanvasHeight = photoEditorView.source.layoutParams.height + // We used to obtain the screen dimensions by querying resources.displayMetrics but this value is the + // actual screen size minus the navigation bar height i.e. on a Pixel device we'd get 1794 instead of 1920. + // Given ComposeLoopFrameActivity is full screen, we can rely on the measuredHeight calculation instead. + screenWidth = photoEditorView.source.measuredWidth + screenHeight = photoEditorView.source.measuredHeight if (isScreenTallerThan916(screenWidth, screenHeight)) { normalizedSize = normalizeSizeExportTo916(screenWidth, screenHeight).toSize() return (screenHeight - normalizedSize.height) @@ -1998,7 +1994,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // extract matrix to float array matrixValues matrix.getValues(matrixValues) frame.source.backgroundViewInfo = BackgroundViewInfo( - imageMatrixValues = matrixValues + imageMatrixValues = matrixValues, + scaleType = backgroundImageSource.scaleType ) } @@ -2010,6 +2007,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec matrix.setValues(it.imageMatrixValues) backgroundImageSource.apply { setSuppMatrix(matrix) + scaleType = it.scaleType } } } @@ -2053,7 +2051,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.01f)) { bottom_opaque_bar.visibility = View.GONE photoEditorView.source.scaleType = CENTER_CROP - loadImageWithGlideToDraw(drawable, CenterCrop(), screenWidth, originalCanvasHeight, doAfterUse) + loadImageWithGlideToDraw(drawable, CenterCrop(), screenWidth, screenHeight, doAfterUse) } else { // 2. if the device is taller than 9:16, and image is portrait // just crop the bottom (showing the opaque bar) @@ -2067,7 +2065,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // 3. else, load with fit-center bottom_opaque_bar.visibility = View.VISIBLE photoEditorView.source.scaleType = FIT_CENTER - loadImageWithGlideToDraw(drawable, FitCenter(), screenWidth, originalCanvasHeight, doAfterUse) + loadImageWithGlideToDraw(drawable, FitCenter(), screenWidth, screenHeight, doAfterUse) } } } @@ -2089,7 +2087,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec .listener(provideGlideRequestListener(doAfterUse)) .override(overrideWidth, overrideHeight) .into(photoEditorView.source) - } ?: Glide.with(this@ComposeLoopFrameActivity) .load(drawable) .listener(provideGlideRequestListener(doAfterUse)) diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt index 5f5c1dca3..a9e8a33f6 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameItem.kt @@ -1,6 +1,7 @@ package com.wordpress.stories.compose.story import android.net.Uri +import android.widget.ImageView.ScaleType import com.automattic.photoeditor.views.added.AddedView import com.automattic.photoeditor.views.added.AddedViewList import com.wordpress.stories.compose.frame.StorySaveEvents.SaveResultReason @@ -29,7 +30,8 @@ data class StoryFrameItem( ) { @Serializable data class BackgroundViewInfo( - val imageMatrixValues: FloatArray + val imageMatrixValues: FloatArray, + val scaleType: ScaleType ) @Serializable From d2aa125add64012b37387bcb3976fcce0e670377 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 12:03:36 -0300 Subject: [PATCH 058/101] added more comments --- .../stories/compose/ComposeLoopFrameActivity.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index aa301a49e..e2cf81b81 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2045,8 +2045,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } withContext(Dispatchers.Main) { - // 1. if the image being loaded matches the aspect ratio of the device screen, then align to top - // (no parts would actually be cropped, given the matching aspect ratio it should fit) + // 1. if the image being loaded matches the aspect ratio of the device screen, use center_crop + // no parts would actually be cropped, given the matching aspect ratio it should fit + // except the opaque bar given we're normalizing to 9:16 on export val drawableAspectRatio = calculateAspectRatioForDrawable(drawable) if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.01f)) { bottom_opaque_bar.visibility = View.GONE @@ -2059,10 +2060,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec (drawable.intrinsicHeight > drawable.intrinsicWidth) ) { bottom_opaque_bar.visibility = View.VISIBLE - photoEditorView.source.scaleType = FIT_START + photoEditorView.source.scaleType = FIT_START // this aligns to top so there's no top black bar loadImageWithGlideToDraw(drawable, null, normalizedSize.width, normalizedSize.height, doAfterUse) } else { - // 3. else, load with fit-center + // 3. else, load with fit-center (black bars on the side that doesn't fit) + // see https://developer.android.com/reference/android/graphics/Matrix.ScaleToFit#CENTER bottom_opaque_bar.visibility = View.VISIBLE photoEditorView.source.scaleType = FIT_CENTER loadImageWithGlideToDraw(drawable, FitCenter(), screenWidth, screenHeight, doAfterUse) From dab1ca8005dc464ac1281957d8c4299584ecba9e Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 12:04:50 -0300 Subject: [PATCH 059/101] setting scaleType and transform on ghost view when exporting, according to passed values on frame.source.backgroundViewInfo --- .../stories/compose/frame/FrameSaveManager.kt | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index 5993deca5..ecfac5b4e 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -7,7 +7,9 @@ import android.graphics.Matrix import android.net.Uri import android.view.View import android.view.ViewGroup.LayoutParams +import android.widget.ImageView.ScaleType.CENTER_CROP import android.widget.ImageView.ScaleType.FIT_CENTER +import android.widget.ImageView.ScaleType.FIT_START import android.widget.RelativeLayout import com.automattic.photoeditor.PhotoEditor import com.automattic.photoeditor.PhotoEditor.OnSaveWithCancelAndProgressListener @@ -292,20 +294,46 @@ class FrameSaveManager( ?: (frame.source as FileBackgroundSource).file val targetView = ghostPhotoEditorView.source + val scaleType = frame.source.backgroundViewInfo?.scaleType // making use of Glide to decode bitmap and get the right orientation automatically // http://bumptech.github.io/glide/doc/getting-started.html#background-threads - val futureTarget = Glide.with(context) - .asBitmap() - .load(uri) - .submit(targetView.measuredWidth, targetView.measuredHeight) + val futureTarget = when (scaleType) { + FIT_START -> + Glide.with(context) + .asBitmap() + .load(uri) + // no transform used when FIT_START, see correlation in ComposeLoopFrameActivity's + // loadImageWithGlideToPrepare() + .submit(targetView.measuredWidth, targetView.measuredHeight) + FIT_CENTER -> + Glide.with(context) + .asBitmap() + .load(uri) + .fitCenter() // we use fitCenter at first (instead of cropping) so we don't lose any information + .submit(targetView.measuredWidth, targetView.measuredHeight) + CENTER_CROP -> + Glide.with(context) + .asBitmap() + .load(uri) + .centerCrop() // we use fitCenter at first (instead of cropping) so we don't lose any information + .submit(targetView.measuredWidth, targetView.measuredHeight) + else -> // default case with no transform needed so futureTarget is initialized, + // but we don't really expect to get this case + Glide.with(context) + .asBitmap() + .load(uri) + .submit(targetView.measuredWidth, targetView.measuredHeight) + } val bitmap = futureTarget.get() targetView.setImageBitmap(bitmap) // IMPORTANT: scaleType and setSuppMatrix should only be called _after_ the bitmap is set on the targetView // by means of targetView.setImageBitmap(). Calling this before will have no effect due to PhotoView's checks. (targetView as BackgroundImageView).apply { - scaleType = FIT_CENTER + frame.source.backgroundViewInfo?.let { + this.scaleType = it.scaleType + } setSuppMatrix(originalMatrix) } From b9cbe5beec138d44d2d78324caf2e7b199b1067c Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 12:20:46 -0300 Subject: [PATCH 060/101] no need to clear the futureTarget after we're done given it's used and discarded by Glide itself once we use it on an actual drawing operation --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index e2cf81b81..4b85b13be 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2038,8 +2038,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec frame, photoEditor.composedCanvas.source as PhotoView ) - // finally, clean target so resources can be freed up - Glide.with(this@ComposeLoopFrameActivity).clear(futureTarget) } } } From e1b71794392ac41eae025591492edf50b43df196 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 15:25:20 -0300 Subject: [PATCH 061/101] using FIT_START for drawables wider than screen, and FIT_CENTER for drawables narrower than the device screen so it always looks centered --- .../stories/compose/ComposeLoopFrameActivity.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 4b85b13be..821a37e4c 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2058,8 +2058,14 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec (drawable.intrinsicHeight > drawable.intrinsicWidth) ) { bottom_opaque_bar.visibility = View.VISIBLE - photoEditorView.source.scaleType = FIT_START // this aligns to top so there's no top black bar - loadImageWithGlideToDraw(drawable, null, normalizedSize.width, normalizedSize.height, doAfterUse) + val transformToUse = if (drawable.intrinsicWidth >= screenWidth) { + photoEditorView.source.scaleType = FIT_START // this aligns to top so there's no top black bar + null + } else { + photoEditorView.source.scaleType = FIT_CENTER + FitCenter() + } + loadImageWithGlideToDraw(drawable, transformToUse, normalizedSize.width, normalizedSize.height, doAfterUse) } else { // 3. else, load with fit-center (black bars on the side that doesn't fit) // see https://developer.android.com/reference/android/graphics/Matrix.ScaleToFit#CENTER From f64aaf6c44f052ccf1c03b1e6975859760133fd2 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 15:27:53 -0300 Subject: [PATCH 062/101] moved normalizedSize calculation up so it's always available --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 821a37e4c..072038891 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -575,8 +575,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // Given ComposeLoopFrameActivity is full screen, we can rely on the measuredHeight calculation instead. screenWidth = photoEditorView.source.measuredWidth screenHeight = photoEditorView.source.measuredHeight + normalizedSize = normalizeSizeExportTo916(screenWidth, screenHeight).toSize() if (isScreenTallerThan916(screenWidth, screenHeight)) { - normalizedSize = normalizeSizeExportTo916(screenWidth, screenHeight).toSize() return (screenHeight - normalizedSize.height) } else { return 0 From c4347301e78a6fed1b14f9bd8bc8b773ec59f368 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 16:34:36 -0300 Subject: [PATCH 063/101] fixed lint warnings --- .../stories/compose/ComposeLoopFrameActivity.kt | 15 ++++++++------- .../stories/compose/frame/FrameSaveManager.kt | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 072038891..d47aec644 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -262,7 +262,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private var screenSizeRatio: Float = TARGET_RATIO_9_16 private lateinit var normalizedSize: Size - private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { Log.d("ComposeLoopFrame", "onServiceConnected()") @@ -2013,14 +2012,14 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun loadImageWithGlideToPrepare(frame: StoryFrameItem) { - val model = (frame.source as? FileBackgroundSource)?.file ?: - (frame.source as UriBackgroundSource).contentUri + val model = (frame.source as? FileBackgroundSource)?.file + ?: (frame.source as UriBackgroundSource).contentUri CoroutineScope(Dispatchers.IO).launch { val futureTarget = Glide.with(this@ComposeLoopFrameActivity) .asDrawable() .load(model) - .fitCenter() // we use fitCenter at first (instead of cropping) so we don't lose any information + .fitCenter() // we use fitCenter at first (instead of cropping) so we don't lose any information .submit(screenWidth, screenHeight) // we're not going to export images greater than the screen size val drawable = futureTarget.get() @@ -2033,7 +2032,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // https://github.com/Baseflow/PhotoView/blob/139a9ffeaf70bd628b015374cb6530fcf7d0bcb7/photoview/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java#L279-L289 // if we're all good, doAfter() will be callled on Glide's `onResourceReady`, so // let's setup the ViewMatrix - Handler().post{ + Handler().post { setBackgroundViewInfoOnPhotoView( frame, photoEditor.composedCanvas.source as PhotoView @@ -2059,13 +2058,15 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec ) { bottom_opaque_bar.visibility = View.VISIBLE val transformToUse = if (drawable.intrinsicWidth >= screenWidth) { - photoEditorView.source.scaleType = FIT_START // this aligns to top so there's no top black bar + // this aligns to top so there's no top black bar + photoEditorView.source.scaleType = FIT_START null } else { photoEditorView.source.scaleType = FIT_CENTER FitCenter() } - loadImageWithGlideToDraw(drawable, transformToUse, normalizedSize.width, normalizedSize.height, doAfterUse) + loadImageWithGlideToDraw(drawable, transformToUse, + normalizedSize.width, normalizedSize.height, doAfterUse) } else { // 3. else, load with fit-center (black bars on the side that doesn't fit) // see https://developer.android.com/reference/android/graphics/Matrix.ScaleToFit#CENTER diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index ecfac5b4e..604025dd7 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -310,13 +310,13 @@ class FrameSaveManager( Glide.with(context) .asBitmap() .load(uri) - .fitCenter() // we use fitCenter at first (instead of cropping) so we don't lose any information + .fitCenter() // we use fitCenter at first (instead of cropping) so we don't lose any information .submit(targetView.measuredWidth, targetView.measuredHeight) CENTER_CROP -> Glide.with(context) .asBitmap() .load(uri) - .centerCrop() // we use fitCenter at first (instead of cropping) so we don't lose any information + .centerCrop() // we use fitCenter at first (instead of cropping) so we don't lose any information .submit(targetView.measuredWidth, targetView.measuredHeight) else -> // default case with no transform needed so futureTarget is initialized, // but we don't really expect to get this case From cc4395555a4989a17dee7fc880e389d8da006dab Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 16:54:12 -0300 Subject: [PATCH 064/101] we want the bottom_opaque_bar visible at all times while editing, it will be hidden when calculating a height of 0 for it --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index d47aec644..228591c90 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2046,8 +2046,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // no parts would actually be cropped, given the matching aspect ratio it should fit // except the opaque bar given we're normalizing to 9:16 on export val drawableAspectRatio = calculateAspectRatioForDrawable(drawable) + bottom_opaque_bar.visibility = View.VISIBLE if (isAspectRatioSimilarByPercentage(drawableAspectRatio, screenSizeRatio, 0.01f)) { - bottom_opaque_bar.visibility = View.GONE photoEditorView.source.scaleType = CENTER_CROP loadImageWithGlideToDraw(drawable, CenterCrop(), screenWidth, screenHeight, doAfterUse) } else { @@ -2056,7 +2056,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (isScreenTallerThan916(screenWidth, screenHeight) && (drawable.intrinsicHeight > drawable.intrinsicWidth) ) { - bottom_opaque_bar.visibility = View.VISIBLE val transformToUse = if (drawable.intrinsicWidth >= screenWidth) { // this aligns to top so there's no top black bar photoEditorView.source.scaleType = FIT_START @@ -2070,7 +2069,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } else { // 3. else, load with fit-center (black bars on the side that doesn't fit) // see https://developer.android.com/reference/android/graphics/Matrix.ScaleToFit#CENTER - bottom_opaque_bar.visibility = View.VISIBLE photoEditorView.source.scaleType = FIT_CENTER loadImageWithGlideToDraw(drawable, FitCenter(), screenWidth, screenHeight, doAfterUse) } From 4fc499a8d8ff87da08603e6ed2d5b5ea8de8cfdd Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 10 Mar 2021 16:58:42 -0300 Subject: [PATCH 065/101] added newline before EOF --- stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt index 2f3b3c8f3..b821edb1a 100644 --- a/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt +++ b/stories/src/main/java/com/wordpress/stories/util/ViewUtils.kt @@ -82,4 +82,4 @@ fun isAspectRatioSimilarByPercentage(aspectRatio1: Float, aspectRatio2: Float, p } fun getSizeRatio(originalWidth: Int, originalHeight: Int): Float { return (originalWidth.toFloat() / originalHeight.toFloat()) -} \ No newline at end of file +} From ad2fca55179d42acb9ec6954ccbdab4203a9e365 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 18 Mar 2021 14:57:03 +0100 Subject: [PATCH 066/101] setting backgroundImageBlurred visibility when turning TexturView on/off --- .../java/com/automattic/photoeditor/views/PhotoEditorView.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index 185600740..f15e77bec 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -292,11 +292,13 @@ class PhotoEditorView : RelativeLayout { internal fun turnTextureViewOn() { backgroundImage.visibility = View.INVISIBLE + backgroundImageBlurred.visibility = View.INVISIBLE autoFitTextureView.visibility = View.VISIBLE } internal fun turnTextureViewOff() { backgroundImage.visibility = View.VISIBLE + backgroundImageBlurred.visibility = View.VISIBLE autoFitTextureView.visibility = View.INVISIBLE } @@ -304,11 +306,13 @@ class PhotoEditorView : RelativeLayout { backgroundImage.visibility = autoFitTextureView.visibility.also { autoFitTextureView.visibility = backgroundImage.visibility } + backgroundImageBlurred.visibility = backgroundImage.visibility return autoFitTextureView.visibility == View.VISIBLE } internal fun turnTextureAndImageViewOff() { backgroundImage.visibility = View.INVISIBLE + backgroundImageBlurred.visibility = View.INVISIBLE autoFitTextureView.visibility = View.INVISIBLE } From 2ca984ae965006f3a85a3a5f5b087facb10b3061 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 22 Mar 2021 11:51:52 +0100 Subject: [PATCH 067/101] udpated EventBus library version --- app/build.gradle | 10 +++++----- stories/build.gradle | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8d7ee80a7..d7bc22f86 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,11 +10,11 @@ android { signingConfigs { release { - if (rootProject.hasProperty("storeFile") - && rootProject.hasProperty("storePassword") - && rootProject.hasProperty("keyAlias") + if (rootProject.hasProperty("storeFile") + && rootProject.hasProperty("storePassword") + && rootProject.hasProperty("keyAlias") && rootProject.hasProperty("keyPassword") - ) { + ) { storeFile file("${rootDir}/${rootProject.storeFile}") storePassword rootProject.storePassword keyAlias rootProject.keyAlias @@ -78,7 +78,7 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.10.0' kapt 'com.github.bumptech.glide:compiler:4.10.0' - implementation 'org.greenrobot:eventbus:3.1.1' + implementation 'org.greenrobot:eventbus:3.2.0' implementation 'io.sentry:sentry-android:1.7.27' // this dependency is not required if you are already using your own diff --git a/stories/build.gradle b/stories/build.gradle index 1c02f8175..4eadf145d 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -54,7 +54,7 @@ dependencies { implementation 'jp.wasabeef:glide-transformations:4.3.0' - implementation 'org.greenrobot:eventbus:3.1.1' + implementation 'org.greenrobot:eventbus:3.2.0' implementation project(path: ':photoeditor') From c710bfd8f79efaed0fd4f223fc328424774b509d Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 9 Apr 2021 22:03:02 +0200 Subject: [PATCH 068/101] make sure to run onStoryFrameSelected in onLoadFromIntent, so first frame is loaded when loading from inetnt --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index e55e4b134..04460744e 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -726,6 +726,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } else if (!partialCameraOperationInProgress && storyIndexToSelect != StoryRepository.DEFAULT_NONE_SELECTED && StoryRepository.getStoryAtIndex(storyIndexToSelect).frames.isNotEmpty()) { storyViewModel.loadStory(storyIndexToSelect) + onStoryFrameSelected(oldIndex = StoryRepository.DEFAULT_FRAME_NONE_SELECTED, newIndex = 0) showGenericAnnouncementDialogWhenReady = true return } From 863fae17dfe86df6dced395b79d2588d85447d29 Mon Sep 17 00:00:00 2001 From: Alex Forcier Date: Thu, 15 Apr 2021 14:04:09 +0900 Subject: [PATCH 069/101] Tweak color picker bottom sheet min height --- stories/src/main/res/values/dimens.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/res/values/dimens.xml b/stories/src/main/res/values/dimens.xml index 8fc8459a4..dc1aaba97 100644 --- a/stories/src/main/res/values/dimens.xml +++ b/stories/src/main/res/values/dimens.xml @@ -16,7 +16,7 @@ - 250dp + 275dp 75dp 40dp 4dp From 0f27600b99857d1ab76b326b81818cc31713cc10 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 16 Apr 2021 20:51:14 +0200 Subject: [PATCH 070/101] added null check for frameSaveResult[index] before accessing its members --- .../wordpress/stories/compose/story/StoryRepository.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt index 859fb7701..d72041135 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt @@ -124,9 +124,11 @@ object StoryRepository { // iterate over the StorySaveResult, check their indexes, and set the corresponding frame result if (isStoryIndexValid(storyIndex)) { for (index in 0..saveResult.frameSaveResult.size - 1) { - val frameIdxToSet = saveResult.frameSaveResult[index].frameIndex - stories[storyIndex].frames[frameIdxToSet].saveResultReason = - saveResult.frameSaveResult[index].resultReason + if (saveResult.frameSaveResult[index] != null) { + val frameIdxToSet = saveResult.frameSaveResult[index].frameIndex + stories[storyIndex].frames[frameIdxToSet].saveResultReason = + saveResult.frameSaveResult[index].resultReason + } } } } From edb1714a58531ac1fbff02a60f058ea308c53ee4 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 19 Apr 2021 14:00:57 +0200 Subject: [PATCH 071/101] replace throw error with logging error instead --- .../java/com/daasuu/mp4compose/composer/Mp4ComposerEngine.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mp4compose/src/main/java/com/daasuu/mp4compose/composer/Mp4ComposerEngine.kt b/mp4compose/src/main/java/com/daasuu/mp4compose/composer/Mp4ComposerEngine.kt index d327480a2..35f5b83fc 100755 --- a/mp4compose/src/main/java/com/daasuu/mp4compose/composer/Mp4ComposerEngine.kt +++ b/mp4compose/src/main/java/com/daasuu/mp4compose/composer/Mp4ComposerEngine.kt @@ -246,9 +246,7 @@ internal class Mp4ComposerEngine { mediaExtractor = null } } catch (e: RuntimeException) { - // Too fatal to make alive the app, because it may leak native resources. - - throw Error("Could not shutdown mediaExtractor, codecs and mediaMuxer pipeline.", e) + Log.e(TAG, "Could not shutdown mediaExtractor, codecs and mediaMuxer pipeline.", e) } try { From 8f6ae05225d489c3263f4ff592ae392b5998e608 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 20 Apr 2021 11:12:13 +0200 Subject: [PATCH 072/101] always reorder AddedViewList according to z-index order in getter --- .../com/automattic/photoeditor/PhotoEditor.kt | 17 +++++++++++++++++ .../photoeditor/views/PhotoEditorView.kt | 4 ++++ .../photoeditor/views/added/AddedViewList.kt | 10 ++++++++++ 3 files changed, 31 insertions(+) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt b/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt index 23f493bd1..4bb8aeac3 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt @@ -1046,9 +1046,26 @@ class PhotoEditor private constructor(builder: Builder) : } fun getViewsAdded(): AddedViewList { + // always make sure to return a freshly z-index-ordered AddedViewList + reorderAddedViewListAccordingToZIndex() return addedViews } + private fun reorderAddedViewListAccordingToZIndex() { + val reorderedAddedViewList = AddedViewList() + for (oneView in parentView.zIndexOrderedAddedViews) { + when (oneView.tag) { + TEXT, EMOJI, ViewType.STICKER_ANIMATED -> { + addedViews.getAddedViewForNativeView(oneView)?.let { + reorderedAddedViewList.add(it) + } + } + } + } + addedViews.clear() + addedViews.addAll(reorderedAddedViewList) + } + /** * Builder pattern to define [PhotoEditor] Instance */ diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt index f15e77bec..95eae4acb 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/PhotoEditorView.kt @@ -18,6 +18,7 @@ import android.widget.ProgressBar import android.widget.RelativeLayout import androidx.annotation.RequiresApi import androidx.appcompat.widget.AppCompatImageView +import androidx.core.view.children import com.automattic.photoeditor.OnSaveBitmap import com.automattic.photoeditor.R.styleable import com.automattic.photoeditor.views.background.fixed.BackgroundImageView @@ -91,6 +92,9 @@ class PhotoEditorView : RelativeLayout { val listeners: ArrayList get() = surfaceListeners + val zIndexOrderedAddedViews: Sequence + get() = children + constructor(context: Context) : super(context) { init(null) } diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedViewList.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedViewList.kt index bac2c7da9..ccba9dd89 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedViewList.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedViewList.kt @@ -45,6 +45,16 @@ class AddedViewList : ArrayList() { } return -1 } + fun getAddedViewForNativeView(element: View): AddedView? { + for (oneView in this) { + oneView.view?.let { + if (it == element) { + return oneView + } + } + } + return null + } fun containsAnyAddedViewsOfType(type: ViewType): Boolean { for (oneView: AddedView in this) { From 549fadff0ea2c8c9ff3c5f35b60f44b9e695effc Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 20 Apr 2021 13:02:59 +0200 Subject: [PATCH 073/101] remove setImageMatrix override, don't call onBitmapLoaded --- .../views/background/fixed/BackgroundImageView.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt index b60d64d34..36a2495a0 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt @@ -49,11 +49,6 @@ class BackgroundImageView : PhotoView { mOnImageChangedListener?.onBitmapLoaded(bitmap) } - override fun setImageMatrix(matrix: Matrix) { - super.setImageMatrix(matrix) - mOnImageChangedListener?.onBitmapLoaded(bitmap) - } - override fun setImageState(state: IntArray, merge: Boolean) { super.setImageState(state, merge) mOnImageChangedListener?.onBitmapLoaded(bitmap) From 0584295a693c698c5c41e51bcb05f51bcef3e1df Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 20 Apr 2021 13:09:17 +0200 Subject: [PATCH 074/101] removed unused import --- .../photoeditor/views/background/fixed/BackgroundImageView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt index 36a2495a0..fd78d7df4 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/background/fixed/BackgroundImageView.kt @@ -3,7 +3,6 @@ package com.automattic.photoeditor.views.background.fixed import android.content.Context import android.content.res.ColorStateList import android.graphics.Bitmap -import android.graphics.Matrix import android.graphics.PorterDuff import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable From 706b1928f11b765a3e77d4b8df54293bcae927c6 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 20 Apr 2021 14:15:30 +0200 Subject: [PATCH 075/101] update current selected frame in onStart() to make sure touch listeners are added on added views --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 04460744e..557bcfc82 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -597,6 +597,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec showGenericAnnouncementDialogWhenReady = false genericAnnouncementDialogProvider?.showGenericAnnouncementDialog() } + if (storyViewModel.getCurrentStorySize() > 0) { + showCurrentSelectedFrame() + } } private fun setupStoryViewModelObservers() { From e818942cc8941b294badaeb89c3d2f8a1ed097e8 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sun, 25 Apr 2021 09:32:07 +0200 Subject: [PATCH 076/101] inflate added views when saving if these are not populated yet --- .../com/automattic/photoeditor/PhotoEditor.kt | 47 ++++++++++++------- .../photoeditor/views/added/AddedView.kt | 4 +- .../stories/compose/frame/FrameSaveManager.kt | 19 ++++++++ 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt b/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt index 4bb8aeac3..2e05e0745 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt @@ -241,7 +241,8 @@ class PhotoEditor private constructor(builder: Builder) : fun addText( text: String, textStyler: TextStyler? = null, - isViewBeingReadded: Boolean = false + isViewBeingReadded: Boolean = false, + addTouchListener: Boolean = false ): View? { brushDrawingView.brushDrawingMode = false val view: View? @@ -253,9 +254,11 @@ class PhotoEditor private constructor(builder: Builder) : // textInputTv.setLayerType(View.LAYER_TYPE_SOFTWARE, null) - val multiTouchListenerInstance = getNewMultitouchListener() // newMultiTouchListener - setGestureControlOnMultiTouchListener(this, ViewType.TEXT, multiTouchListenerInstance) - setOnTouchListener(multiTouchListenerInstance) + if (addTouchListener) { + val multiTouchListenerInstance = getNewMultitouchListener() // newMultiTouchListener + setGestureControlOnMultiTouchListener(this, ViewType.TEXT, multiTouchListenerInstance) + setOnTouchListener(multiTouchListenerInstance) + } addViewToParent(this, ViewType.TEXT) // now open TextEditor right away if this is new text being added @@ -365,11 +368,16 @@ class PhotoEditor private constructor(builder: Builder) : fun addViewToParentWithTouchListener(addedView: AddedView) { addedView.view?.let { addViewToParentWithTouchListener(it, addedView.viewType, addedView.uri) - } ?: buildViewFromAddedViewInfo(addedView.viewInfo, addedView.viewType) + } ?: buildViewFromAddedViewInfo(addedView.viewInfo, addedView.viewType, true) + .also { addedView.view = it } } @SuppressLint("ClickableViewAccessibility") - private fun buildViewFromAddedViewInfo(addedViewInfo: AddedViewInfo, viewType: ViewType): View? { + fun buildViewFromAddedViewInfo( + addedViewInfo: AddedViewInfo, + viewType: ViewType, + addTouchListener: Boolean + ): View? { var view: View? = null when (viewType) { EMOJI -> { @@ -381,18 +389,21 @@ class PhotoEditor private constructor(builder: Builder) : // the actual calculated text size as obtained from the view is expressed in px. emojiTextView?.setTextSize(TypedValue.COMPLEX_UNIT_PX, addedViewInfo.addedViewTextInfo.fontSizePx) - val multiTouchListenerInstance = getNewMultitouchListener(it) // newMultiTouchListener - setGestureControlOnMultiTouchListener(it, viewType, multiTouchListenerInstance) - it.touchableArea?.setOnTouchListener(multiTouchListenerInstance) + if (addTouchListener) { + val multiTouchListenerInstance = getNewMultitouchListener(it) // newMultiTouchListener + setGestureControlOnMultiTouchListener(it, viewType, multiTouchListenerInstance) + it.touchableArea?.setOnTouchListener(multiTouchListenerInstance) + } } } TEXT -> { // create TEXT view layout val textStyler = TextStyler.from(addedViewInfo.addedViewTextInfo) view = addText( - text = addedViewInfo.addedViewTextInfo.text, - textStyler = textStyler, - isViewBeingReadded = true + text = addedViewInfo.addedViewTextInfo.text, + textStyler = textStyler, + isViewBeingReadded = true, + addTouchListener = addTouchListener ) } } @@ -783,7 +794,9 @@ class PhotoEditor private constructor(builder: Builder) : } else -> { clearHelperBox() - filterCollection.add(GlWatermarkFilter(BitmapUtil.createBitmapFromView(v.view), viewPositionInfo)) + v.view?.let { + filterCollection.add(GlWatermarkFilter(BitmapUtil.createBitmapFromView(it), viewPositionInfo)) + } } } } @@ -935,9 +948,11 @@ class PhotoEditor private constructor(builder: Builder) : } else -> { clearHelperBox() - filterCollection.add( - GlWatermarkFilter(BitmapUtil.createBitmapFromView(oneView.view), viewPositionInfo) - ) + oneView.view?.let { + filterCollection.add( + GlWatermarkFilter(BitmapUtil.createBitmapFromView(it), viewPositionInfo) + ) + } } } } diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedView.kt b/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedView.kt index f9667e589..70132b688 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedView.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/views/added/AddedView.kt @@ -16,7 +16,7 @@ import kotlinx.serialization.Serializer @Serializable class AddedView( - @Transient val view: View? = null, + @Transient var view: View? = null, val viewType: ViewType, var viewInfo: AddedViewInfo, @Serializable(with = UriSerializer::class) @@ -73,7 +73,7 @@ class AddedView( it.translationX, it.translationY, it.scaleX, - getTextInfoFromActualView(view, viewType) + getTextInfoFromActualView(it, viewType) ) } } diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index 5748128f5..083f820db 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -120,12 +120,31 @@ class FrameSaveManager( return listFiles } + private suspend fun inflateAddedViews(frame: StoryFrameItem) { + // when editing a Story, if a frame has not been selected to be shown in PhotoEditor, + // the views are never inflated. Let's make sure we do that before saving, otherwise + // we'll miss added views in the export. + withContext(Dispatchers.Main) { + for (addedView in frame.addedViews) { + if (addedView.view == null) { + addedView.view = photoEditor.buildViewFromAddedViewInfo( + addedView.viewInfo, + addedView.viewType, + false + ) + } + } + } + } + private suspend fun saveStoryFrame( context: Context, frame: StoryFrameItem, frameIndex: FrameIndex ): File? { var frameFile: File? = null + // make sure to inflate and add AddedViews to parentView for this frame if this hasn't been done yet + inflateAddedViews(frame) when (frame.frameItemType) { is VIDEO -> { // - if we have addedViews then we need to process the vido with mp4composer From b248297076fc0b7fd19596345f1d405f7e3201c4 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 26 Apr 2021 19:20:56 +0200 Subject: [PATCH 077/101] call setOpaqueBarHeight in the OnApplyWindowsInsetsListener to make sure to upate the bar's height to the latest dimensions --- .../stories/compose/ComposeLoopFrameActivity.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 557bcfc82..ac05c2e00 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -356,6 +356,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec workingAreaRect = calculateWorkingArea() photoEditor.updateWorkAreaRect(workingAreaRect) bottomOpaqueBarHeight = preCalculateOpaqueBarHeight() + setOpaqueBarHeight() screenSizeRatio = getSizeRatio(screenWidth, screenHeight) delete_view.addBottomOffset(bottomNavigationBarMargin) delete_slide_view.addBottomOffset(bottomNavigationBarMargin) @@ -583,8 +584,12 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun setOpaqueBarHeight() { + bottom_opaque_bar.layoutParams.height = bottomOpaqueBarHeight + } + + private fun showOpaqueBarIfNeeded() { if (bottomOpaqueBarHeight > 0) { - bottom_opaque_bar.layoutParams.height = bottomOpaqueBarHeight + bottom_opaque_bar.visibility = View.VISIBLE } else { bottom_opaque_bar.visibility = View.GONE } @@ -1640,7 +1645,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun showStoryFrameSelector() { setOpaqueBarHeight() - bottom_opaque_bar.visibility = View.VISIBLE + showOpaqueBarIfNeeded() (bottom_strip_view as StoryFrameSelectorFragment).show() } From 54ba2441d764a871fdfee26266b256fa2786b267 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 26 Apr 2021 20:08:54 +0200 Subject: [PATCH 078/101] only show current selected frame in onStart() if there's no pending TextureView request to show --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 557bcfc82..55dcef617 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -597,7 +597,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec showGenericAnnouncementDialogWhenReady = false genericAnnouncementDialogProvider?.showGenericAnnouncementDialog() } - if (storyViewModel.getCurrentStorySize() > 0) { + if (storyViewModel.getCurrentStorySize() > 0 && + !launchCameraRequestPending && + !launchVideoPlayerRequestPending) { showCurrentSelectedFrame() } } From 5801c633e32a64e5173dc3513d2b036fdfba0a15 Mon Sep 17 00:00:00 2001 From: Alex Forcier Date: Tue, 27 Apr 2021 07:27:17 +0900 Subject: [PATCH 079/101] Use textColorPrimary for color picker bottom sheet text --- stories/src/main/res/layout/color_picker_bottom_sheet.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stories/src/main/res/layout/color_picker_bottom_sheet.xml b/stories/src/main/res/layout/color_picker_bottom_sheet.xml index 3df8ec605..c6e213a76 100644 --- a/stories/src/main/res/layout/color_picker_bottom_sheet.xml +++ b/stories/src/main/res/layout/color_picker_bottom_sheet.xml @@ -24,7 +24,8 @@ android:layout_marginBottom="@dimen/color_picker_bottom_sheet_label_bottom_margin" android:textSize="18sp" android:textStyle="bold" - android:textColor="#DE000000" + android:textColor="?android:attr/textColorPrimary" + android:alpha="0.87" android:text="@string/color_picker_label_text" /> Date: Tue, 27 Apr 2021 07:07:12 +0200 Subject: [PATCH 080/101] add touchlistener by default on newly added text added views --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index c2d126eee..35647d8dd 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -1278,7 +1278,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun addNewText() { - photoEditor.addText("") + photoEditor.addText("", addTouchListener = true) } private fun testEmoji() { From 4440028b1336cb01b90fcc3b3616e81835751a0a Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 28 Apr 2021 14:19:47 +0200 Subject: [PATCH 081/101] only set opaque bar height if the calculated result is greater than 0 --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 35647d8dd..26faf6150 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -584,7 +584,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun setOpaqueBarHeight() { - bottom_opaque_bar.layoutParams.height = bottomOpaqueBarHeight + if (bottomOpaqueBarHeight > 0) { + bottom_opaque_bar.layoutParams.height = bottomOpaqueBarHeight + } } private fun showOpaqueBarIfNeeded() { From 6b22a5a73a9bb2de736a1abef667743dafd5c9ca Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 28 Apr 2021 15:05:00 +0200 Subject: [PATCH 082/101] make button INVISIBLE instead of GONE so it occupies layout space, plus make it plain white so it's clearer behing the schrim --- .../stories/compose/story/StoryFrameSelectorFragment.kt | 2 +- .../src/main/res/drawable/save_button_background_disabled.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt index 22d8d2210..61c950041 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorFragment.kt @@ -195,7 +195,7 @@ class StoryFrameSelectorFragment : Fragment() { } fun hide() { - view?.visibility = View.GONE + view?.visibility = View.INVISIBLE } fun hideAddFrameControl() { diff --git a/stories/src/main/res/drawable/save_button_background_disabled.xml b/stories/src/main/res/drawable/save_button_background_disabled.xml index 84bf4e99f..7563986f9 100644 --- a/stories/src/main/res/drawable/save_button_background_disabled.xml +++ b/stories/src/main/res/drawable/save_button_background_disabled.xml @@ -1,5 +1,5 @@ - + From d009dd6c3e4dc710932a6089a351b7f7768e6166 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 29 Apr 2021 13:38:51 +0200 Subject: [PATCH 083/101] only allow delete mode toggling when not in error handling mode --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 26faf6150..68f6e9781 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2151,7 +2151,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } override fun onCurrentFrameTapped() { - toggleDeleteSlideMode() + val currentlyErrored = storyViewModel.anyOfCurrentStoryFramesIsErrored() + if (!currentlyErrored) { + toggleDeleteSlideMode() + } } override fun onStoryFrameLongPressed(oldIndex: Int, newIndex: Int) { From cd35c1ced7e7503fa25350b710776bb9bd6da9a4 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 29 Apr 2021 14:08:27 +0200 Subject: [PATCH 084/101] check index when loading from error notification --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 4 ++-- .../com/wordpress/stories/compose/story/StoryRepository.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 68f6e9781..5f790bda8 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -748,8 +748,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec checkForLowSpaceAndShowDialog() } else if (intent.hasExtra(KEY_STORY_SAVE_RESULT)) { val storySaveResult = intent.getParcelableExtra(KEY_STORY_SAVE_RESULT) as StorySaveResult? - if (storySaveResult != null && - StoryRepository.getStoryAtIndex(storySaveResult.storyIndex).frames.isNotEmpty()) { + if (storySaveResult != null && StoryRepository.isStoryIndexValid(storySaveResult.storyIndex) + && StoryRepository.getStoryAtIndex(storySaveResult.storyIndex).frames.isNotEmpty()) { // dismiss the error notification intent.action?.let { val notificationManager = NotificationManagerCompat.from(this) diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt index d72041135..8018573b3 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryRepository.kt @@ -54,7 +54,7 @@ object StoryRepository { } } - private fun isStoryIndexValid(storyIndex: StoryIndex): Boolean { + fun isStoryIndexValid(storyIndex: StoryIndex): Boolean { return storyIndex > DEFAULT_NONE_SELECTED && stories.size > storyIndex } From a164ab17bc513408767bb1515f09d8387f2b7da1 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 29 Apr 2021 14:13:18 +0200 Subject: [PATCH 085/101] changed && placement to comply with checkstyle rules --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 5f790bda8..85488922f 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -748,8 +748,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec checkForLowSpaceAndShowDialog() } else if (intent.hasExtra(KEY_STORY_SAVE_RESULT)) { val storySaveResult = intent.getParcelableExtra(KEY_STORY_SAVE_RESULT) as StorySaveResult? - if (storySaveResult != null && StoryRepository.isStoryIndexValid(storySaveResult.storyIndex) - && StoryRepository.getStoryAtIndex(storySaveResult.storyIndex).frames.isNotEmpty()) { + if (storySaveResult != null && StoryRepository.isStoryIndexValid(storySaveResult.storyIndex) && + StoryRepository.getStoryAtIndex(storySaveResult.storyIndex).frames.isNotEmpty()) { // dismiss the error notification intent.action?.let { val notificationManager = NotificationManagerCompat.from(this) From a49a6f39dcfb0228b6b4b34fc0e0e187acd557da Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sat, 1 May 2021 15:38:17 +0200 Subject: [PATCH 086/101] make addedviews reordering and accessing methods synchronized to make sure no access happens in between reordering calls --- .../src/main/java/com/automattic/photoeditor/PhotoEditor.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt b/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt index 2e05e0745..3a551a80d 100644 --- a/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt +++ b/photoeditor/src/main/java/com/automattic/photoeditor/PhotoEditor.kt @@ -1060,12 +1060,14 @@ class PhotoEditor private constructor(builder: Builder) : return (addedViews.size > 0) } + @Synchronized fun getViewsAdded(): AddedViewList { // always make sure to return a freshly z-index-ordered AddedViewList reorderAddedViewListAccordingToZIndex() return addedViews } + @Synchronized private fun reorderAddedViewListAccordingToZIndex() { val reorderedAddedViewList = AddedViewList() for (oneView in parentView.zIndexOrderedAddedViews) { From 80d811c247e5bce2bf7ded13487e61355eafc904 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sat, 1 May 2021 15:39:57 +0200 Subject: [PATCH 087/101] added reattachAddedViewsAfterSaving parameter to saveStoryFrame which is true for retries, so given the user stays on the Screen we should keep the added views being shown after saving --- .../stories/compose/frame/FrameSaveManager.kt | 31 ++++++++++++++----- .../stories/compose/frame/FrameSaveService.kt | 8 +++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt index 083f820db..a241d15a4 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveManager.kt @@ -66,7 +66,8 @@ class FrameSaveManager( suspend fun saveStory( context: Context, - frames: List + frames: List, + isRetry: Boolean ): List { // calling the listener here so the progress notification initializes itself properly and // shows really how many Story frame pages we're going to save @@ -74,7 +75,7 @@ class FrameSaveManager( // first, save all images async and wait val savedImages = saveLoopFramesAsyncAwait( - context, frames, IMAGE, IMAGE_CONCURRENCY_LIMIT + context, frames, IMAGE, IMAGE_CONCURRENCY_LIMIT, isRetry ) yield() @@ -82,7 +83,7 @@ class FrameSaveManager( // now, save all videos async and wait - this process is intense so only allow for 3 videos to be processed // concurrently val savedVideos = saveLoopFramesAsyncAwait( - context, frames, VIDEO(), VIDEO_CONCURRENCY_LIMIT + context, frames, VIDEO(), VIDEO_CONCURRENCY_LIMIT, isRetry ) return savedImages + savedVideos @@ -98,7 +99,8 @@ class FrameSaveManager( context: Context, frames: List, frameItemType: StoryFrameItemType, - concurrencyLimit: Int + concurrencyLimit: Int, + reattachAddedViewsAfterSaving: Boolean ): List { // don't process more than 5 Story Pages concurrently val concurrencyLimitSemaphore = Semaphore(concurrencyLimit) @@ -110,7 +112,7 @@ class FrameSaveManager( // see above - we only want to save frames of frameItemType if (frame.frameItemType.isSameType(frameItemType)) { yield() - return@withPermit saveStoryFrame(context, frame, index) + return@withPermit saveStoryFrame(context, frame, index, reattachAddedViewsAfterSaving) } else { return@withPermit null } @@ -140,7 +142,8 @@ class FrameSaveManager( private suspend fun saveStoryFrame( context: Context, frame: StoryFrameItem, - frameIndex: FrameIndex + frameIndex: FrameIndex, + reattachAddedViewsAfterSaving: Boolean ): File? { var frameFile: File? = null // make sure to inflate and add AddedViews to parentView for this frame if this hasn't been done yet @@ -153,6 +156,9 @@ class FrameSaveManager( if (frame.addedViews.isNotEmpty() || frame.source is UriBackgroundSource) { frameFile = saveVideoFrame(frame, frameIndex) releaseAddedViewsAfterSnapshot(frame) + if (reattachAddedViewsAfterSaving) { + reattachAddedViewsToOriginalParent(frame) + } } else { // don't process the video but return the original file if no added views in this Story frame frameFile = (frame.source as FileBackgroundSource).file @@ -186,6 +192,9 @@ class FrameSaveManager( // are thrown) given it internally does check whether the parent contains the view before // attempting to remove it releaseAddedViewsAfterSnapshot(frame) + if (reattachAddedViewsAfterSaving) { + reattachAddedViewsToOriginalParent(frame) + } } } } @@ -218,7 +227,6 @@ class FrameSaveManager( } } - releaseAddedViewsAfterSnapshot(frame) Glide.with(context).clear(futureTargetPair.first) Glide.with(context).clear(futureTargetPair.second) @@ -232,6 +240,15 @@ class FrameSaveManager( } } + private suspend fun reattachAddedViewsToOriginalParent(frame: StoryFrameItem) { + withContext(Dispatchers.Main) { + for (addedView in frame.addedViews) { + // photoEditor.composedCanvas.addView(addedView.view) + photoEditor.addViewToParentWithTouchListener(addedView) + } + } + } + private suspend fun saveVideoFrame( frame: StoryFrameItem, frameIndex: FrameIndex diff --git a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveService.kt b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveService.kt index 77000c0b2..66819743a 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveService.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/frame/FrameSaveService.kt @@ -203,7 +203,8 @@ class FrameSaveService : Service() { val frameFileList = storySaveProcessor.saveStory( this, - frames + frames, + storySaveProcessor.storySaveResult.isRetry ) storySaveProcessor.timeTracker.end() storySaveProcessor.updateStorySaveResultTimeElapsed() @@ -387,9 +388,10 @@ class FrameSaveService : Service() { suspend fun saveStory( context: Context, - frames: List + frames: List, + isRetry: Boolean ): List { - return frameSaveManager.saveStory(context, frames) + return frameSaveManager.saveStory(context, frames, isRetry) } fun onCancel() { From 0b224be1d1af75412fd38183265c346edc1d2352 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sat, 1 May 2021 16:02:47 +0200 Subject: [PATCH 088/101] Revert "only allow delete mode toggling when not in error handling mode" This reverts commit d009dd6c3e4dc710932a6089a351b7f7768e6166. --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 85488922f..48b617baa 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2151,10 +2151,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } override fun onCurrentFrameTapped() { - val currentlyErrored = storyViewModel.anyOfCurrentStoryFramesIsErrored() - if (!currentlyErrored) { - toggleDeleteSlideMode() - } + toggleDeleteSlideMode() } override fun onStoryFrameLongPressed(oldIndex: Int, newIndex: Int) { From 31d673e42cd7e98431af0aeb0cddaaf44333c117 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sat, 1 May 2021 16:56:02 +0200 Subject: [PATCH 089/101] show/hide retry button when toggling delete mode, otherwise they overlap --- .../stories/compose/ComposeLoopFrameActivity.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 48b617baa..8e930e6a3 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2168,10 +2168,26 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun toggleDeleteSlideMode() { + val selectedFrame = storyViewModel.getSelectedFrame() + val isErroredFrame = selectedFrame?.saveResultReason !is SaveSuccess + if (delete_slide_view.visibility == View.VISIBLE) { disableDeleteSlideMode() + + // if we're switching back from delete mode, let's show the retry button if this is an errored frame + if (isErroredFrame) { + showRetryButton() + updateEditMode() + } } else { enableDeleteSlideMode() + + // if we're switching to delete mode, let's hide the retry button if this is an errored frame + // (otherwise they overlap) + if (isErroredFrame) { + hideRetryButton() + updateEditMode() + } } } From e3c505cbd886490095c12031d7a173c600cd4b58 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sat, 1 May 2021 17:16:22 +0200 Subject: [PATCH 090/101] always call updateEditMode() when toggling from/to deleteMode --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 8e930e6a3..5e46816df 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -2177,7 +2177,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // if we're switching back from delete mode, let's show the retry button if this is an errored frame if (isErroredFrame) { showRetryButton() - updateEditMode() } } else { enableDeleteSlideMode() @@ -2186,9 +2185,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // (otherwise they overlap) if (isErroredFrame) { hideRetryButton() - updateEditMode() } } + updateEditMode() } private fun enableDeleteSlideMode() { From 7053875477f8e39386df401a303760e735aacac2 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sun, 2 May 2021 12:19:50 +0200 Subject: [PATCH 091/101] removed unused resource --- stories/src/main/res/values/colors.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/stories/src/main/res/values/colors.xml b/stories/src/main/res/values/colors.xml index edc20729a..83af402c5 100644 --- a/stories/src/main/res/values/colors.xml +++ b/stories/src/main/res/values/colors.xml @@ -11,7 +11,6 @@ #c9356e #FFFFFF - #66FFFFFF #000000 #888888 #C3C4C7 From a778379e1621c1d3cab4a5a057d00780daee5f8a Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 3 May 2021 19:36:47 +0200 Subject: [PATCH 092/101] add EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED flag to trigger provided media picker upon creation --- app/src/main/java/com/automattic/loop/StoryComposerActivity.kt | 2 ++ .../com/automattic/loop/photopicker/PhotoPickerActivity.java | 2 ++ .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 3 +++ 3 files changed, 7 insertions(+) diff --git a/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt b/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt index 4b68b8531..79d3c383b 100644 --- a/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt +++ b/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt @@ -120,6 +120,8 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), requestCodes.PHOTO_PICKER = RequestCodes.PHOTO_PICKER requestCodes.EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED = PhotoPickerActivity.EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED + requestCodes.EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED = + PhotoPickerActivity.EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED requestCodes.EXTRA_MEDIA_URIS = PhotoPickerActivity.EXTRA_MEDIA_URIS } diff --git a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java index b933e1aab..39de0fa44 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java @@ -44,6 +44,8 @@ public class PhotoPickerActivity extends AppCompatActivity public static final String EXTRA_MEDIA_SOURCE = "media_source"; public static final String EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED = "launch_wpstories_camera_requested"; + public static final String EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED + = "launch_wpstories_media_picker_requested"; public static final String LOCAL_POST_ID = "local_post_id"; diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 26faf6150..24ebb9fd3 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -771,6 +771,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec ) addFramesToStoryFromMediaUriList(uriList) setDefaultSelectionAndUpdateBackgroundSurfaceUI(uriList) + } else if (intent.hasExtra(requestCodes.EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED)) { + showMediaPicker() } } @@ -2313,6 +2315,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // if not properly initialized) lateinit var EXTRA_MEDIA_URIS: String lateinit var EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED: String + lateinit var EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED: String } companion object { From 6aea8a6c3cf6a53f31d1eec56fe60263ffea66ce Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Mon, 3 May 2021 16:08:08 -0400 Subject: [PATCH 093/101] Temporarily remove fetchstyle & configure plugins --- .circleci/config.yml | 70 ++++++++++++++++++++++---------------------- build.gradle | 9 +++--- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 409eb62bf..50fbb53ee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,7 +15,7 @@ commands: jobs: Lint: - executor: + executor: name: android/default api-version: "29" steps: @@ -35,7 +35,7 @@ jobs: - android/save-gradle-cache - android/save-lint-results Unit Tests: - executor: + executor: name: android/default api-version: "29" steps: @@ -47,42 +47,42 @@ jobs: command: ./gradlew --stacktrace -PtestsMaxHeapSize=1536m testRelease - android/save-gradle-cache - android/save-test-results - Installable Build: - executor: - name: android/default - api-version: "29" - steps: - - checkout - - run: - name: Apply Configuration - command: ./gradlew applyConfiguration - - android/restore-gradle-cache - - run: - name: Build APK - command: | - if [ -n "$CIRCLE_PULL_REQUEST" ]; then - PR_NUMBER=$(basename $CIRCLE_PULL_REQUEST) - PREFIX="pr-${PR_NUMBER}" - else - PREFIX="$CIRCLE_BRANCH" - fi - VERSION_NAME="${PREFIX}-build-${CIRCLE_BUILD_NUM}" - echo "export VERSION_NAME=$VERSION_NAME" >> $BASH_ENV - - ./gradlew --stacktrace app:assembleRelease -PversionName="$VERSION_NAME" - - android/save-gradle-cache - - run: - name: Prepare APK - command: | - mkdir -p Artifacts - mv app/build/outputs/apk/release/app-release.apk "Artifacts/Loop-${VERSION_NAME}.apk" - - store_artifacts: - path: Artifacts - destination: Artifacts +# Installable Build: +# executor: +# name: android/default +# api-version: "29" +# steps: +# - checkout +# - run: +# name: Apply Configuration +# command: ./gradlew applyConfiguration +# - android/restore-gradle-cache +# - run: +# name: Build APK +# command: | +# if [ -n "$CIRCLE_PULL_REQUEST" ]; then +# PR_NUMBER=$(basename $CIRCLE_PULL_REQUEST) +# PREFIX="pr-${PR_NUMBER}" +# else +# PREFIX="$CIRCLE_BRANCH" +# fi +# VERSION_NAME="${PREFIX}-build-${CIRCLE_BUILD_NUM}" +# echo "export VERSION_NAME=$VERSION_NAME" >> $BASH_ENV +# +# ./gradlew --stacktrace app:assembleRelease -PversionName="$VERSION_NAME" +# - android/save-gradle-cache +# - run: +# name: Prepare APK +# command: | +# mkdir -p Artifacts +# mv app/build/outputs/apk/release/app-release.apk "Artifacts/Loop-${VERSION_NAME}.apk" +# - store_artifacts: +# path: Artifacts +# destination: Artifacts workflows: loop: jobs: - Lint - Unit Tests - - Installable Build +# - Installable Build diff --git a/build.gradle b/build.gradle index 92c92e372..1327ffaf1 100644 --- a/build.gradle +++ b/build.gradle @@ -6,24 +6,23 @@ buildscript { repositories { google() jcenter() - maven { url "https://dl.bintray.com/automattic/maven/" } maven { url "https://jitpack.io" } } dependencies { classpath 'com.android.tools.build:gradle:3.5.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" - classpath 'com.automattic.android:fetchstyle:1.1' + //classpath 'com.automattic.android:fetchstyle:1.1' classpath 'io.sentry:sentry-android-gradle-plugin:1.7.27' - classpath 'com.automattic.android:configure:0.3.0' + //classpath 'com.automattic.android:configure:0.3.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } -apply plugin: 'com.automattic.android.fetchstyle' -apply plugin: 'com.automattic.android.configure' +//apply plugin: 'com.automattic.android.fetchstyle' +//apply plugin: 'com.automattic.android.configure' allprojects { apply plugin: 'checkstyle' From 20c7464edac8896ca5883b79827e26a20c8a7f35 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 4 May 2021 20:39:22 +0200 Subject: [PATCH 094/101] if ComposeLoopFrameActivity was called with EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED and we get a cancel in onActivityResult, finish the Activity if Story is empty. Also make sure to call onLoadFromIntent once only, use firstIntentLoaded = true --- .../compose/ComposeLoopFrameActivity.kt | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 24ebb9fd3..15a673baf 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -564,7 +564,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (selectedFrameIndex < storyViewModel.getCurrentStorySize()) { storyViewModel.setSelectedFrame(selectedFrameIndex) } - } else if (storyIndexToSelect != StoryRepository.DEFAULT_NONE_SELECTED) { + } else { onLoadFromIntent(intent) } } @@ -616,12 +616,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // if no frames in Story, finish // note momentarily there will be times when this LiveData is triggered while permissions are // being requested so, don't proceed if that is the case - if (storyViewModel.getCurrentStorySize() == 0 && - firstIntentLoaded && !permissionsRequestForCameraInProgress) { - // finally, delete the captured media - deleteCapturedMedia() - finish() - } + deleteCaptureMediaAndFinishWhenEmptyStory() }) storyViewModel.onSelectedFrameIndex.observe(this, Observer { selectedFrameIndexChange -> @@ -637,6 +632,15 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec }) } + private fun deleteCaptureMediaAndFinishWhenEmptyStory() { + if (storyViewModel.getCurrentStorySize() == 0 && + firstIntentLoaded && !permissionsRequestForCameraInProgress) { + // finally, delete the captured media + deleteCapturedMedia() + finish() + } + } + private fun updateUiStateForAudioMuted(frameIndex: Int) { if (frameIndex == storyViewModel.getSelectedFrameIndex()) { updateSoundControl() @@ -773,6 +777,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec setDefaultSelectionAndUpdateBackgroundSurfaceUI(uriList) } else if (intent.hasExtra(requestCodes.EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED)) { showMediaPicker() + firstIntentLoaded = true } } @@ -884,6 +889,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } launchCameraPreviewWithSurfaceSafeguard() } + } else if (intent.hasExtra(requestCodes.EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED)) { + // if coming from the PHOTO_PICKER with a cancel action, and we launched with + // EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED to start the Story with, we should cancel. + deleteCaptureMediaAndFinishWhenEmptyStory() } } } From a56f5f4903d3512939640e90154ba05536d69740 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 5 May 2021 21:16:47 +0200 Subject: [PATCH 095/101] moved the firstIntentLoaded flag as first step when onLoadFromIntent gets called to make 100% sure it's set whenever it's called --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 26faf6150..1c8d4f0f8 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -484,7 +484,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec override fun onBackgroundSurfaceManagerReady() { if (savedInstanceState == null && !firstIntentLoaded) { onLoadFromIntent(intent) - firstIntentLoaded = true } if (launchCameraRequestPending) { @@ -729,6 +728,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } protected open fun onLoadFromIntent(intent: Intent) { + firstIntentLoaded = true val partialCameraOperationInProgress = intent.hasExtra(requestCodes.EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED) || permissionsRequestForCameraInProgress From 02321de1dd26c3aafc015042e123f693c5231977 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 6 May 2021 15:40:38 +0200 Subject: [PATCH 096/101] move deleteCapturedMedia() to OK listener for frame removal dialog --- .../com/wordpress/stories/compose/ComposeLoopFrameActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 31abe736e..cafd28b16 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -617,8 +617,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // being requested so, don't proceed if that is the case if (storyViewModel.getCurrentStorySize() == 0 && firstIntentLoaded && !permissionsRequestForCameraInProgress) { - // finally, delete the captured media - deleteCapturedMedia() finish() } }) @@ -1085,6 +1083,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec listener = object : FrameSaveErrorDialogOk { override fun OnOkClicked(dialog: DialogFragment) { dialog.dismiss() + // first of all, delete the backing captured media for this slide + deleteCapturedMedia() if (storyViewModel.getCurrentStorySize() == 1) { // discard the whole story safelyDiscardCurrentStoryAndCleanUpIntent() From 71b308b3871904373fa4abbfd7f6f2e5c01797f6 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 11 May 2021 13:15:44 +0200 Subject: [PATCH 097/101] method does not delete captured media anymore, so renamed method --- .../stories/compose/ComposeLoopFrameActivity.kt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index 21932cbad..e4503c703 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -613,9 +613,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun setupStoryViewModelObservers() { storyViewModel.uiState.observe(this, Observer { // if no frames in Story, finish - // note momentarily there will be times when this LiveData is triggered while permissions are - // being requested so, don't proceed if that is the case - deleteCaptureMediaAndFinishWhenEmptyStory() + finishWhenEmptyStory() }) storyViewModel.onSelectedFrameIndex.observe(this, Observer { selectedFrameIndexChange -> @@ -631,11 +629,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec }) } - private fun deleteCaptureMediaAndFinishWhenEmptyStory() { + private fun finishWhenEmptyStory() { if (storyViewModel.getCurrentStorySize() == 0 && firstIntentLoaded && !permissionsRequestForCameraInProgress) { - // finally, delete the captured media - deleteCapturedMedia() finish() } } @@ -892,7 +888,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } else if (intent.hasExtra(requestCodes.EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED)) { // if coming from the PHOTO_PICKER with a cancel action, and we launched with // EXTRA_LAUNCH_WPSTORIES_MEDIA_PICKER_REQUESTED to start the Story with, we should cancel. - deleteCaptureMediaAndFinishWhenEmptyStory() + finishWhenEmptyStory() } } } From d58e553a15468e8f3ba22182a54b150877a14da1 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 11 May 2021 16:27:49 +0200 Subject: [PATCH 098/101] remove zero-frame finisher observer --- .../wordpress/stories/compose/ComposeLoopFrameActivity.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt index e0ecce5c0..7084bff3a 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -611,13 +611,6 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun setupStoryViewModelObservers() { - storyViewModel.uiState.observe(this, Observer { - // if no frames in Story, finish - // note momentarily there will be times when this LiveData is triggered while permissions are - // being requested so, don't proceed if that is the case - deleteCaptureMediaAndFinishWhenEmptyStory() - }) - storyViewModel.onSelectedFrameIndex.observe(this, Observer { selectedFrameIndexChange -> updateSelectedFrameControls(selectedFrameIndexChange.first, selectedFrameIndexChange.second) }) From 92c9d542c79657afe1da756674a40c5d88f06682 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 11 May 2021 16:57:48 +0200 Subject: [PATCH 099/101] fixed uninitialized access to lateinit prop for media picker --- app/src/main/java/com/automattic/loop/StoryComposerActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt b/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt index 79d3c383b..b36002fe3 100644 --- a/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt +++ b/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt @@ -60,9 +60,9 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), get() = Dispatchers.Main + job override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) setSnackbarProvider(this) setMediaPickerProvider(this) + super.onCreate(savedInstanceState) setNotificationExtrasLoader(this) setMetadataProvider(this) setStoryDiscardListener(this) // optionally listen to discard events From 23232879b2beec1486b13a30200aeba45cf03d46 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 11 May 2021 17:01:39 +0200 Subject: [PATCH 100/101] updated onStoryDiscarded listener in demo app --- app/src/main/java/com/automattic/loop/StoryComposerActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt b/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt index b36002fe3..8dc2b2bdd 100644 --- a/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt +++ b/app/src/main/java/com/automattic/loop/StoryComposerActivity.kt @@ -166,6 +166,7 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), override fun onStoryDiscarded() { // example: do any cleanup you may need here Toast.makeText(this, "Story has been discarded!", Toast.LENGTH_SHORT).show() + finish() } override fun onFrameRemove(storyIndex: StoryIndex, storyFrameIndex: Int) { From 417afd824f14a125a91bf6fa677a84602a9f77b9 Mon Sep 17 00:00:00 2001 From: mzorz Date: Fri, 14 May 2021 03:43:24 -0300 Subject: [PATCH 101/101] Update README.md --- README.md | 73 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index c96c4f1a1..824ff6c4d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,41 @@ -# stories-android -Stories concept app -WordPress stories library +## The Stories library + +**Note:** For a comprehensive explanation of everything in the library (including entry points and the library's API), please read the project's [wiki here](https://github.com/Automattic/stories-android/wiki). + +## Build Instructions ## + +1. Make sure you've installed [Android Studio](https://developer.android.com/studio/index.html). +1. `git clone git@github.com:Automattic/stories-android.git` in the folder of your preference. +1. `cd stories-android` to enter the working directory. +1. `cp gradle.properties-example gradle.properties` to set up the sample app properties file. Specifically, you can use `stories.use.cameraX = true` to use the CameraX underlying implementation, or `false` to use the Camera2 implementation (we recommend using CameraX and our plans are to eventually only stick to CameraX once it's stable) +1. In Android Studio, open the project from the local repository. This will auto-generate `local.properties` with the SDK location. +1. Go to Tools → AVD Manager and create an emulated device. +1. Run. + + +### Getting started +In order to integrate the stories library, you must include the following in your project: +``` + implementation project(path: ':photoeditor') + implementation project(path: ':stories') +``` + +Implement these: +- SnackbarProvider +- call `setSnackbarProvider()` as in the example Stories demo app. + +- MediaPickerProvider +- call `setMediaPickerProvider()` as in the example Stories demo app. +- remember to override `setupRequestCodes()` and set the right request codes as per the host app so it works seamlessly and media can be fed into the Composer by the externally provided MediaPicker. + +### Entry points +The ComposeLoopFrameActivity super class will attempt to capture different Activity entry points such as `onCreate()` (which is handled in the `onLoadFromIntent()` override) or `onActivityResult()`. As such, you'll be able to use the behavior by passing requestCode `requestCodes.PHOTO_PICKER` and a combination of one of the following extras to trigger each of the modes the composer can be presented in: +- EXTRA_MEDIA_URIS: if `providerHandlesMediaPickerResult()` returns false, these extra will consist of a list of media Uris that will be added each one as a new Story slide to the composer. +- EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED: if the previous extra is not present and this extra is passed, then the camera capture mode will be presented, and the user will then have the ability to capture a still image or a video, which will be added to their Story as a new slide. + +In the Loop demo app, we initially trigger the camera capture mode for a new Story (you can try this by tapping on the FAB), and then rely on the PhotoPicker implementation to allow the user to keep adding pictures (note they can also trigger the capture mode from there as well). For an example of a using a different entry point for its initial state, you can check WordPress Android's [StoryComposerActivity](https://github.com/wordpress-mobile/WordPress-Android/blob/develop/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt)'s `handleMediaPickerIntentData()` method, given in this case the Media picker is presented first, and the Story composer is initialized later with the selected background media constituting one Story slide each. + # Code style @@ -38,39 +72,6 @@ From there, add and enable the custom configuration file, located at [config/che You can run ktlint using `./gradlew ktlint`, and you can also run `./gradlew ktlintFormat` for auto-formatting. There is no IDEA plugin (like Checkstyle's) at this time. -## The Stories library - -In order to integrate the stories library, you must include the following in your project: -``` - implementation project(path: ':photoeditor') - implementation project(path: ':stories') -``` - -Implement these: -- SnackbarProvider -- call `setSnackbarProvider()` as in the example Stories demo app. - -- MediaPickerProvider -- call `setMediaPickerProvider()` as in the example Stories demo app. -- remember to override `setupRequestCodes()` and set the right request codes as per the host app so it works seamlessly and media can be fed into the Composer by the externally provided MediaPicker. - -### Entry points -The ComposeLoopFrameActivity super class will attempt to capture different Activity entry points such as `onCreate()` (which is handled in the `onLoadFromIntent()` override) or `onActivityResult()`. As such, you'll be able to use the behavior by passing requestCode `requestCodes.PHOTO_PICKER` and a combination of one of the following extras to trigger each of the modes the composer can be presented in: -- EXTRA_MEDIA_URIS: if `providerHandlesMediaPickerResult()` returns false, these extra will consist of a list of media Uris that will be added each one as a new Story slide to the composer. -- EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED: if the previous extra is not present and this extra is passed, then the camera capture mode will be presented, and the user will then have the ability to capture a still image or a video, which will be added to their Story as a new slide. - -In the Loop demo app, we initially trigger the camera capture mode for a new Story (you can try this by tapping on the FAB), and then rely on the PhotoPicker implementation to allow the user to keep adding pictures (note they can also trigger the capture mode from there as well). For an example of a using a different entry point for its initial state, you can check WordPress Android's [StoryComposerActivity](https://github.com/wordpress-mobile/WordPress-Android/blob/develop/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt)'s `handleMediaPickerIntentData()` method, given in this case the Media picker is presented first, and the Story composer is initialized later with the selected background media constituting one Story slide each. - -## Build Instructions ## - -1. Make sure you've installed [Android Studio](https://developer.android.com/studio/index.html). -1. `git clone git@github.com:Automattic/stories-android.git` in the folder of your preference. -1. `cd stories-android` to enter the working directory. -1. `cp gradle.properties-example gradle.properties` to set up the sample app properties file. Specifically, you can use `stories.use.cameraX = true` to use the CameraX underlying implementation, or `false` to use the Camera2 implementation -1. In Android Studio, open the project from the local repository. This will auto-generate `local.properties` with the SDK location. -1. Go to Tools → AVD Manager and create an emulated device. -1. Run. - ## License ## The stories module and the Loop concept app are Open Source projects covered by the