From d069b3ab0bd0aa9b5b64b094b5ff84ee484cb31e Mon Sep 17 00:00:00 2001 From: faranjit Date: Mon, 4 Jan 2021 23:45:40 +0300 Subject: [PATCH 1/7] started implementing viewBinding --- app/build.gradle | 3 + .../java/com/automattic/loop/LoopActivity.kt | 21 ++++++ .../java/com/automattic/loop/MainActivity.kt | 69 ++++++++++--------- .../loop/photopicker/PhotoPickerActivity.java | 41 ++++++----- app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/toolbar.xml | 51 +++++++------- build.gradle | 2 +- 7 files changed, 110 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/com/automattic/loop/LoopActivity.kt diff --git a/app/build.gradle b/app/build.gradle index b7824e988..15cb5b24e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,6 +20,9 @@ android { } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + viewBinding { + enabled = true + } buildTypes { release { minifyEnabled false diff --git a/app/src/main/java/com/automattic/loop/LoopActivity.kt b/app/src/main/java/com/automattic/loop/LoopActivity.kt new file mode 100644 index 000000000..539b40e03 --- /dev/null +++ b/app/src/main/java/com/automattic/loop/LoopActivity.kt @@ -0,0 +1,21 @@ +package com.automattic.loop + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding + +/** + * Created by Bulent Turkmen on 4.01.2021. + */ +abstract class LoopActivity : AppCompatActivity() { + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = inflateBinding() + setContentView(binding.root) + } + + abstract fun inflateBinding(): VB +} diff --git a/app/src/main/java/com/automattic/loop/MainActivity.kt b/app/src/main/java/com/automattic/loop/MainActivity.kt index bf18c068f..fb5a0879b 100644 --- a/app/src/main/java/com/automattic/loop/MainActivity.kt +++ b/app/src/main/java/com/automattic/loop/MainActivity.kt @@ -4,13 +4,13 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity import androidx.navigation.Navigation -import com.automattic.photoeditor.util.PermissionUtils import com.automattic.loop.StoryComposerActivity.Companion.KEY_EXAMPLE_METADATA import com.automattic.loop.StoryComposerActivity.Companion.KEY_STORY_INDEX +import com.automattic.loop.databinding.ActivityMainBinding import com.automattic.loop.intro.IntroActivity import com.automattic.loop.photopicker.PhotoPickerActivity +import com.automattic.photoeditor.util.PermissionUtils import com.google.android.material.snackbar.Snackbar import com.wordpress.stories.compose.frame.FrameSaveNotifier import com.wordpress.stories.compose.frame.FrameSaveNotifier.Companion.getNotificationIdForError @@ -19,23 +19,24 @@ import com.wordpress.stories.compose.frame.StorySaveEvents.StorySaveProcessStart import com.wordpress.stories.compose.frame.StorySaveEvents.StorySaveResult import com.wordpress.stories.compose.story.StoryRepository import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT -import kotlinx.android.synthetic.main.activity_main.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionListener { +class MainActivity : LoopActivity(), MainFragment.OnFragmentInteractionListener { override fun onFragmentInteraction(uri: Uri) { // TODO: change OnFragmentInteractionListener for something relevant to our needs } + override fun inflateBinding(): ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - setSupportActionBar(toolbar) + + setSupportActionBar(binding.toolbar) EventBus.getDefault().register(this) - activity_main.setOnApplyWindowInsetsListener { view, insets -> + binding.root.setOnApplyWindowInsetsListener { view, insets -> // remember the insetTop as margin to all controls appearing at the top of the screen for full screen // screens (i.e. ComposeLoopFrameActivity) (application as Loop).setStatusBarHeight(insets.systemWindowInsetTop) @@ -47,26 +48,28 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList finish() } - fab.setOnClickListener { view -> - fab.isEnabled = false - // NOTE: we want to start with camera capture mode in this demo app, so we pass the - // bundle with the corresponding parameter. - // If we had URIs to start the composer already populated with them, we'd use - // EXTRA_MEDIA_URIS and a list of URIs for media items we want to use as Story frames. - val bundle = Bundle() - bundle.putBoolean(PhotoPickerActivity.EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED, true) - Navigation.findNavController(this, R.id.nav_host_fragment) - .navigate(R.id.action_mainFragment_to_composeLoopFrameActivity, bundle) + binding.fab.run { + setOnClickListener { + isEnabled = false + // NOTE: we want to start with camera capture mode in this demo app, so we pass the + // bundle with the corresponding parameter. + // If we had URIs to start the composer already populated with them, we'd use + // EXTRA_MEDIA_URIS and a list of URIs for media items we want to use as Story frames. + val bundle = Bundle() + bundle.putBoolean(PhotoPickerActivity.EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED, true) + Navigation.findNavController(this@MainActivity, R.id.nav_host_fragment) + .navigate(R.id.action_mainFragment_to_composeLoopFrameActivity, bundle) + } } } override fun onResume() { super.onResume() - fab.isEnabled = true + binding.fab.isEnabled = true } override fun onSupportNavigateUp() = - Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() + Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() override fun onDestroy() { EventBus.getDefault().unregister(this) @@ -82,27 +85,28 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList val payloadString = it.getString(KEY_EXAMPLE_METADATA) val storyIndex = it.getInt(KEY_STORY_INDEX) Toast.makeText( - this, "Payload is: " + payloadString + " - index: " + storyIndex, - Toast.LENGTH_SHORT) - .show() + this, "Payload is: " + payloadString + " - index: " + storyIndex, + Toast.LENGTH_SHORT + ) + .show() } if (event.isSuccess()) { val text = String.format( - getString(R.string.story_saving_snackbar_finished_successfully), - StoryRepository.getStoryAtIndex(event.storyIndex).title + getString(R.string.story_saving_snackbar_finished_successfully), + StoryRepository.getStoryAtIndex(event.storyIndex).title ) Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show() } else { // show snackbar and add the PendingIntent with the StorySaveResult as a Serialized object if errors val errorText = String.format( - getString(R.string.story_saving_snackbar_finished_with_error), - StoryRepository.getStoryAtIndex(event.storyIndex).title + getString(R.string.story_saving_snackbar_finished_with_error), + StoryRepository.getStoryAtIndex(event.storyIndex).title ) val snackbarMessage = FrameSaveNotifier.buildSnackbarErrorMessage( - this, - event.frameSaveResult.count { it.resultReason is SaveError }, - errorText + this, + event.frameSaveResult.count { it.resultReason is SaveError }, + errorText ) val snackbar = Snackbar.make(findViewById(android.R.id.content), snackbarMessage, Snackbar.LENGTH_LONG) snackbar.setAction(R.string.story_saving_failed_quick_action_manage) { view -> @@ -116,7 +120,8 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList // from tapping on MANAGE on the snackbar (otherwise they'll be able to discard the // errored story but the error notification will remain existing in the system dashboard) intent.action = getNotificationIdForError( - StoryComposerActivity.BASE_FRAME_MEDIA_ERROR_NOTIFICATION_ID, event.storyIndex).toString() + "" + StoryComposerActivity.BASE_FRAME_MEDIA_ERROR_NOTIFICATION_ID, event.storyIndex + ).toString() + "" startActivity(intent) } @@ -128,8 +133,8 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList fun onStorySaveStart(event: StorySaveProcessStart) { EventBus.getDefault().removeStickyEvent(event) val text = String.format( - getString(R.string.story_saving_snackbar_started), - StoryRepository.getStoryAtIndex(event.storyIndex).title + getString(R.string.story_saving_snackbar_started), + StoryRepository.getStoryAtIndex(event.storyIndex).title ) Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show() } 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..4e413d66d 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java @@ -16,22 +16,24 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import androidx.core.view.GestureDetectorCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; -import com.automattic.loop.photopicker.utils.CameraIntentUtils; +import com.automattic.loop.LoopActivity; import com.automattic.loop.R; +import com.automattic.loop.databinding.PhotoPickerActivityBinding; +import com.automattic.loop.databinding.ToolbarBinding; +import com.automattic.loop.photopicker.utils.CameraIntentUtils; import com.automattic.loop.util.WPMediaUtils; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.util.Collections; import java.util.List; - -public class PhotoPickerActivity extends AppCompatActivity +public class PhotoPickerActivity extends LoopActivity implements PhotoPickerFragment.PhotoPickerListener { private static final String PICKER_FRAGMENT_TAG = "picker_fragment_tag"; private static final String KEY_MEDIA_CAPTURE_PATH = "media_capture_path"; @@ -74,24 +76,28 @@ public static PhotoPickerMediaSource fromString(String strSource) { } } + @NotNull @Override public PhotoPickerActivityBinding inflateBinding() { + return PhotoPickerActivityBinding.inflate(getLayoutInflater()); + } + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); // inside your activity (if you did not enable transitions in your theme) getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + super.onCreate(savedInstanceState); + // set an enter transition (slide up from bottom) getWindow().setEnterTransition(new Slide(Gravity.BOTTOM)); // set exit transition (slide down from top) getWindow().setExitTransition(new Slide(Gravity.TOP)); - setContentView(R.layout.photo_picker_activity); + ToolbarBinding toolbarBinding = ToolbarBinding.bind(binding.getRoot()); + setContentView(binding.getRoot()); mSwipeDetector = new GestureDetectorCompat(this, new FlingGestureListener()); - Toolbar toolbar = findViewById(R.id.toolbar); - // toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp); - setSupportActionBar(toolbar); + setSupportActionBar(toolbarBinding.toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); @@ -130,7 +136,7 @@ private PhotoPickerFragment getPickerFragment() { } @Override - protected void onSaveInstanceState(Bundle outState) { + protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable(PhotoPickerFragment.ARG_BROWSER_TYPE, mBrowserType); // outState.putInt(LOCAL_POST_ID, mLocalPostId); @@ -177,10 +183,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { case RequestCodes.PICTURE_LIBRARY: case RequestCodes.VIDEO_LIBRARY: if (data != null) { - if (data != null) { - doMediaUrisSelected(WPMediaUtils.retrieveMediaUris(data), - PhotoPickerMediaSource.ANDROID_PICKER); - } + doMediaUrisSelected(WPMediaUtils.retrieveMediaUris(data), + PhotoPickerMediaSource.ANDROID_PICKER); } break; // user took a photo with the device camera @@ -216,12 +220,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { private void launchCamera() { CameraIntentUtils.launchCamera(this, getApplicationContext().getPackageName(), - new CameraIntentUtils.LaunchCameraCallback() { - @Override - public void onMediaCapturePathReady(String mediaCapturePath) { - mMediaCapturePath = mediaCapturePath; - } - }); + mediaCapturePath -> mMediaCapturePath = mediaCapturePath); } private void launchWPStoriesCamera() { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index dc686158a..e2c2680e4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -47,4 +47,4 @@ android:tint="@color/colorPrimary" app:srcCompat="@drawable/ic_add_white_24dp"/> - \ No newline at end of file + diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml index 842324e1f..e1ac08014 100644 --- a/app/src/main/res/layout/toolbar.xml +++ b/app/src/main/res/layout/toolbar.xml @@ -1,28 +1,31 @@ - + android:layout_height="wrap_content"> - + - + + + + diff --git a/build.gradle b/build.gradle index 2e5459990..2c0b42907 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' + classpath 'com.android.tools.build:gradle:3.6.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" classpath 'com.automattic.android:fetchstyle:1.1' From 575f47a4dc32bfc9e9d38bcaf9dbb12ec4479b4b Mon Sep 17 00:00:00 2001 From: faranjit Date: Tue, 5 Jan 2021 18:32:17 +0300 Subject: [PATCH 2/7] viewbinding implementing --- .../java/com/automattic/loop/LoopActivity.kt | 21 - .../java/com/automattic/loop/MainActivity.kt | 10 +- .../java/com/automattic/loop/MainFragment.kt | 22 +- .../automattic/loop/intro/IntroActivity.kt | 7 +- .../automattic/loop/intro/IntroFragment.kt | 28 +- .../loop/intro/IntroPagerAdapter.kt | 2 +- .../loop/intro/IntroPagerFragment.kt | 20 +- .../loop/intro/IntroPagerTitleFragment.kt | 22 +- .../loop/intro/PermissionRequestFragment.kt | 15 +- .../loop/photopicker/PhotoPickerActivity.java | 34 +- .../loop/photopicker/PhotoPickerAdapter.java | 82 +- .../loop/photopicker/PhotoPickerFragment.java | 98 +- app/src/main/res/layout/fragment_main.xml | 2 +- stories/build.gradle | 6 + .../stories/ByActivityViewBinding.kt | 52 + .../stories/ByFragmentViewBinding.kt | 53 + .../wordpress/stories/ViewBindingActivity.kt | 24 + .../wordpress/stories/ViewBindingFragment.kt | 27 + .../compose/ComposeLoopFrameActivity.kt | 1109 +++++++++-------- .../src/main/res/layout/activity_composer.xml | 2 +- 20 files changed, 907 insertions(+), 729 deletions(-) delete mode 100644 app/src/main/java/com/automattic/loop/LoopActivity.kt create mode 100644 stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt create mode 100644 stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt create mode 100644 stories/src/main/java/com/wordpress/stories/ViewBindingActivity.kt create mode 100644 stories/src/main/java/com/wordpress/stories/ViewBindingFragment.kt diff --git a/app/src/main/java/com/automattic/loop/LoopActivity.kt b/app/src/main/java/com/automattic/loop/LoopActivity.kt deleted file mode 100644 index 539b40e03..000000000 --- a/app/src/main/java/com/automattic/loop/LoopActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.automattic.loop - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import androidx.viewbinding.ViewBinding - -/** - * Created by Bulent Turkmen on 4.01.2021. - */ -abstract class LoopActivity : AppCompatActivity() { - protected lateinit var binding: VB - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - binding = inflateBinding() - setContentView(binding.root) - } - - abstract fun inflateBinding(): VB -} diff --git a/app/src/main/java/com/automattic/loop/MainActivity.kt b/app/src/main/java/com/automattic/loop/MainActivity.kt index fb5a0879b..893503107 100644 --- a/app/src/main/java/com/automattic/loop/MainActivity.kt +++ b/app/src/main/java/com/automattic/loop/MainActivity.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity import androidx.navigation.Navigation import com.automattic.loop.StoryComposerActivity.Companion.KEY_EXAMPLE_METADATA import com.automattic.loop.StoryComposerActivity.Companion.KEY_STORY_INDEX @@ -19,17 +20,18 @@ import com.wordpress.stories.compose.frame.StorySaveEvents.StorySaveProcessStart import com.wordpress.stories.compose.frame.StorySaveEvents.StorySaveResult import com.wordpress.stories.compose.story.StoryRepository import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT +import com.wordpress.stories.viewBinding import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -class MainActivity : LoopActivity(), MainFragment.OnFragmentInteractionListener { +class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionListener { + private val binding by viewBinding(ActivityMainBinding::inflate) + override fun onFragmentInteraction(uri: Uri) { // TODO: change OnFragmentInteractionListener for something relevant to our needs } - override fun inflateBinding(): ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater) - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -85,7 +87,7 @@ class MainActivity : LoopActivity(), MainFragment.OnFragmen val payloadString = it.getString(KEY_EXAMPLE_METADATA) val storyIndex = it.getInt(KEY_STORY_INDEX) Toast.makeText( - this, "Payload is: " + payloadString + " - index: " + storyIndex, + this, "Payload is: $payloadString - index: $storyIndex", Toast.LENGTH_SHORT ) .show() diff --git a/app/src/main/java/com/automattic/loop/MainFragment.kt b/app/src/main/java/com/automattic/loop/MainFragment.kt index bbf8193bb..f432c67dc 100644 --- a/app/src/main/java/com/automattic/loop/MainFragment.kt +++ b/app/src/main/java/com/automattic/loop/MainFragment.kt @@ -4,9 +4,6 @@ import android.content.Context import android.net.Uri import android.os.Bundle import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup // TODO: Rename parameter arguments, choose names that match @@ -23,7 +20,7 @@ private const val ARG_PARAM2 = "param2" * create an instance of this fragment. * */ -class MainFragment : Fragment() { +class MainFragment : Fragment(R.layout.fragment_main) { // TODO: Rename and change types of parameters private var param1: String? = null private var param2: String? = null @@ -37,11 +34,6 @@ class MainFragment : Fragment() { } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_main, container, false) - } - // TODO: Rename method, update argument and hook method into UI event fun onButtonPressed(uri: Uri) { listener?.onFragmentInteraction(uri) @@ -52,7 +44,7 @@ class MainFragment : Fragment() { if (context is OnFragmentInteractionListener) { listener = context } else { - throw RuntimeException(context.toString() + " must implement OnFragmentInteractionListener") + throw RuntimeException("$context must implement OnFragmentInteractionListener") } } @@ -89,11 +81,11 @@ class MainFragment : Fragment() { // TODO: Rename and change types and number of parameters @JvmStatic fun newInstance(param1: String, param2: String) = - MainFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) + MainFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } } - } } } diff --git a/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt b/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt index b68d50c3a..d0cec90af 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt @@ -6,23 +6,22 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.navigation.Navigation -import com.automattic.photoeditor.util.PermissionUtils import com.automattic.loop.AppPrefs import com.automattic.loop.MainActivity import com.automattic.loop.R +import com.automattic.photoeditor.util.PermissionUtils class IntroActivity : AppCompatActivity(), IntroFragment.OnFragmentInteractionListener, - PermissionRequestFragment.OnFragmentInteractionListener { + PermissionRequestFragment.OnFragmentInteractionListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_intro) window.statusBarColor = ContextCompat.getColor(this, android.R.color.white) showFragment(IntroFragment(), IntroFragment.TAG) } override fun onSupportNavigateUp() = - Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() + Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() override fun onGetStartedPressed() { AppPrefs.setIntroRequired(false) diff --git a/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt b/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt index 252e3e3bc..5a4669241 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt @@ -2,35 +2,35 @@ package com.automattic.loop.intro import android.content.Context import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import androidx.fragment.app.Fragment -import com.automattic.loop.R.layout +import com.automattic.loop.R +import com.automattic.loop.databinding.FragmentIntroBinding +import com.wordpress.stories.viewBinding import kotlinx.android.synthetic.main.fragment_intro.* -class IntroFragment : Fragment() { +class IntroFragment : Fragment(R.layout.fragment_intro) { interface OnFragmentInteractionListener { fun onGetStartedPressed() } - private var listener: OnFragmentInteractionListener? = null + private val binding by viewBinding(FragmentIntroBinding::bind) - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(layout.fragment_intro, container, false) - } + private var listener: OnFragmentInteractionListener? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - get_started_button.setOnClickListener { - listener?.onGetStartedPressed() - } + binding.run { + getStartedButton.setOnClickListener { + listener?.onGetStartedPressed() + } - intro_pager.adapter = IntroPagerAdapter(childFragmentManager) + introPager.adapter = IntroPagerAdapter(childFragmentManager) - // Using a TabLayout for simulating a page indicator strip - tab_layout_indicator.setupWithViewPager(intro_pager, true) + // Using a TabLayout for simulating a page indicator strip + tabLayoutIndicator.setupWithViewPager(intro_pager, true) + } } override fun onAttach(context: Context) { diff --git a/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt b/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt index d737773fa..522669a95 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt @@ -5,7 +5,7 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentPagerAdapter import com.automattic.loop.R -class IntroPagerAdapter(supportFragmentManager: FragmentManager) : FragmentPagerAdapter(supportFragmentManager) { +class IntroPagerAdapter(supportFragmentManager: FragmentManager) : FragmentPagerAdapter(supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { override fun getItem(position: Int): Fragment { return if (position == 0) { IntroPagerTitleFragment.newInstance(R.string.intro_title_text, PROMO_TEXTS[position], diff --git a/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt b/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt index 57dde9c01..2539eeada 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt @@ -1,16 +1,17 @@ package com.automattic.loop.intro import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import androidx.fragment.app.Fragment import com.automattic.loop.R +import com.automattic.loop.databinding.IntroTemplateViewBinding import com.automattic.loop.util.INVALID_RESOURCE_ID import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.intro_title_template_view.* +import com.wordpress.stories.viewBinding + +class IntroPagerFragment : Fragment(R.layout.intro_template_view) { + private val binding by viewBinding(IntroTemplateViewBinding::bind) -class IntroPagerFragment : Fragment() { private var promoText: Int = INVALID_RESOURCE_ID private var backgroundImage: Int = INVALID_RESOURCE_ID @@ -23,19 +24,16 @@ class IntroPagerFragment : Fragment() { } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.intro_template_view, container, false) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) with(view) { Glide.with(context) - .load(backgroundImage) - .into(background_image) + .load(backgroundImage) + .into(binding.backgroundImage) } - promo_text.setText(promoText) + + binding.promoText.setText(promoText) } companion object { diff --git a/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt b/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt index 40760a254..313e1c032 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt @@ -1,16 +1,17 @@ package com.automattic.loop.intro import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import androidx.fragment.app.Fragment import com.automattic.loop.R +import com.automattic.loop.databinding.IntroTitleTemplateViewBinding import com.automattic.loop.util.INVALID_RESOURCE_ID import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.intro_title_template_view.* +import com.wordpress.stories.viewBinding + +class IntroPagerTitleFragment : Fragment(R.layout.intro_title_template_view) { + private val binding by viewBinding(IntroTitleTemplateViewBinding::bind) -class IntroPagerTitleFragment : Fragment() { private var titleText: Int = INVALID_RESOURCE_ID private var promoText: Int = INVALID_RESOURCE_ID private var backgroundImage: Int = INVALID_RESOURCE_ID @@ -25,20 +26,17 @@ class IntroPagerTitleFragment : Fragment() { } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.intro_title_template_view, container, false) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) with(view) { Glide.with(context) - .load(backgroundImage) - .into(background_image) + .load(backgroundImage) + .into(binding.backgroundImage) } - title_text.setText(titleText) - promo_text.setText(promoText) + + binding.titleText.setText(titleText) + binding.promoText.setText(promoText) } companion object { diff --git a/app/src/main/java/com/automattic/loop/intro/PermissionRequestFragment.kt b/app/src/main/java/com/automattic/loop/intro/PermissionRequestFragment.kt index fb7d3afb4..2a139623e 100644 --- a/app/src/main/java/com/automattic/loop/intro/PermissionRequestFragment.kt +++ b/app/src/main/java/com/automattic/loop/intro/PermissionRequestFragment.kt @@ -2,28 +2,25 @@ package com.automattic.loop.intro import android.content.Context import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import androidx.fragment.app.Fragment import com.automattic.loop.R -import kotlinx.android.synthetic.main.fragment_permission.* +import com.automattic.loop.databinding.FragmentPermissionBinding +import com.wordpress.stories.viewBinding -class PermissionRequestFragment : Fragment() { +class PermissionRequestFragment : Fragment(R.layout.fragment_permission) { interface OnFragmentInteractionListener { fun onTurnOnPermissionsPressed() } - private var listener: OnFragmentInteractionListener? = null + private val binding by viewBinding(FragmentPermissionBinding::bind) - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_permission, container, false) - } + private var listener: OnFragmentInteractionListener? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - turn_on_permissions_button.setOnClickListener { + binding.turnOnPermissionsButton.setOnClickListener { listener?.onTurnOnPermissionsPressed() } } 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 4e413d66d..df5a93bff 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java @@ -9,6 +9,7 @@ import android.util.Log; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.Gravity; +import android.view.LayoutInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.Window; @@ -20,20 +21,18 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; -import com.automattic.loop.LoopActivity; import com.automattic.loop.R; import com.automattic.loop.databinding.PhotoPickerActivityBinding; import com.automattic.loop.databinding.ToolbarBinding; import com.automattic.loop.photopicker.utils.CameraIntentUtils; import com.automattic.loop.util.WPMediaUtils; - -import org.jetbrains.annotations.NotNull; +import com.wordpress.stories.ViewBindingActivity; import java.io.File; import java.util.Collections; import java.util.List; -public class PhotoPickerActivity extends LoopActivity +public class PhotoPickerActivity extends ViewBindingActivity implements PhotoPickerFragment.PhotoPickerListener { private static final String PICKER_FRAGMENT_TAG = "picker_fragment_tag"; private static final String KEY_MEDIA_CAPTURE_PATH = "media_capture_path"; @@ -76,24 +75,23 @@ public static PhotoPickerMediaSource fromString(String strSource) { } } - @NotNull @Override public PhotoPickerActivityBinding inflateBinding() { - return PhotoPickerActivityBinding.inflate(getLayoutInflater()); + @NonNull @Override public PhotoPickerActivityBinding inflateBinding(@NonNull LayoutInflater layoutInflater) { + return PhotoPickerActivityBinding.inflate(layoutInflater); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); // inside your activity (if you did not enable transitions in your theme) getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); - super.onCreate(savedInstanceState); - // set an enter transition (slide up from bottom) getWindow().setEnterTransition(new Slide(Gravity.BOTTOM)); // set exit transition (slide down from top) getWindow().setExitTransition(new Slide(Gravity.TOP)); - ToolbarBinding toolbarBinding = ToolbarBinding.bind(binding.getRoot()); - setContentView(binding.getRoot()); + ToolbarBinding toolbarBinding = ToolbarBinding.bind(getBinding().getRoot()); + setContentView(getBinding().getRoot()); mSwipeDetector = new GestureDetectorCompat(this, new FlingGestureListener()); @@ -119,9 +117,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // fragment = PhotoPickerFragment.newInstance(this, mBrowserType, mSite); fragment = PhotoPickerFragment.newInstance(this, mBrowserType); getSupportFragmentManager().beginTransaction() - .replace(R.id.fragment_container, fragment, PICKER_FRAGMENT_TAG) - .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) - .commitAllowingStateLoss(); + .replace(R.id.fragment_container, fragment, PICKER_FRAGMENT_TAG) + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) + .commitAllowingStateLoss(); } else { fragment.setPhotoPickerListener(this); } @@ -353,11 +351,11 @@ private void doMediaUrisSelected(@NonNull List mediaUris, @NonNull PhotoPic // } // }); // } else { - Intent intent = new Intent() - .putExtra(EXTRA_MEDIA_URIS, convertUrisListToStringArray(mediaUris)) - .putExtra(EXTRA_MEDIA_SOURCE, source.name()); - setResult(RESULT_OK, intent); - finish(); + Intent intent = new Intent() + .putExtra(EXTRA_MEDIA_URIS, convertUrisListToStringArray(mediaUris)) + .putExtra(EXTRA_MEDIA_SOURCE, source.name()); + setResult(RESULT_OK, intent); + finish(); // } } diff --git a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java index e9e301bda..d5d417306 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java @@ -12,15 +12,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; +import com.automattic.loop.R; +import com.automattic.loop.databinding.PhotoPickerThumbnailBinding; import com.automattic.loop.photopicker.utils.AniUtils; import com.automattic.loop.photopicker.utils.AniUtils.Duration; -import com.automattic.loop.R; import com.bumptech.glide.Glide; import java.util.ArrayList; @@ -152,9 +152,10 @@ void setLoadThumbnails(boolean loadThumbnails) { } @Override - public ThumbnailViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = mInflater.inflate(R.layout.photo_picker_thumbnail, parent, false); - return new ThumbnailViewHolder(view); + public ThumbnailViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ThumbnailViewHolder( + PhotoPickerThumbnailBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false) + ); } private void updateSelectionCountForPosition(int position, @@ -176,25 +177,25 @@ public void onBindViewHolder(ThumbnailViewHolder holder, int position) { } boolean isSelected = isItemSelected(position); - holder.mTxtSelectionCount.setSelected(isSelected); - holder.mTxtSelectionCount.setVisibility(isSelected || canMultiselect() ? View.VISIBLE : View.GONE); - updateSelectionCountForPosition(position, isSelected, holder.mTxtSelectionCount); + holder.mBinding.textSelectionCount.setSelected(isSelected); + holder.mBinding.textSelectionCount.setVisibility(isSelected || canMultiselect() ? View.VISIBLE : View.GONE); + updateSelectionCountForPosition(position, isSelected, holder.mBinding.textSelectionCount); float scale = isSelected ? SCALE_SELECTED : SCALE_NORMAL; - if (holder.mImgThumbnail.getScaleX() != scale) { - holder.mImgThumbnail.setScaleX(scale); - holder.mImgThumbnail.setScaleY(scale); + if (holder.mBinding.imageThumbnail.getScaleX() != scale) { + holder.mBinding.imageThumbnail.setScaleX(scale); + holder.mBinding.imageThumbnail.setScaleY(scale); } // holder.mVideoOverlay.setVisibility(item.mIsVideo ? View.VISIBLE : View.GONE); - holder.mTxtVideoDuration.setVisibility(item.mIsVideo ? View.VISIBLE : View.GONE); + holder.mBinding.textVideoDuration.setVisibility(item.mIsVideo ? View.VISIBLE : View.GONE); if (mLoadThumbnails) { // mImageManager.load(holder.mImgThumbnail, ImageType.PHOTO, item.mUri.toString(), ScaleType.FIT_CENTER); Glide.with(mContext) .load(item.mUri.toString()) - .fitCenter() - .into(holder.mImgThumbnail); + .fitCenter() + .into(holder.mBinding.imageThumbnail); if (item.mIsVideo) { // SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); @@ -217,12 +218,12 @@ public void onBindViewHolder(ThumbnailViewHolder holder, int position) { } else { duration = duration + "01"; // default to 1 second if even less than a second } - holder.mTxtVideoDuration.setText(duration); + holder.mBinding.textVideoDuration.setText(duration); } else { - holder.mTxtVideoDuration.setText(""); + holder.mBinding.textVideoDuration.setText(""); } } else { - Glide.with(holder.mImgThumbnail.getContext()).clear(holder.mImgThumbnail); + Glide.with(holder.mBinding.imageThumbnail.getContext()).clear(holder.mBinding.imageThumbnail); } } @@ -281,21 +282,21 @@ private void setItemSelected(int position, boolean isSelected, boolean updateAft ThumbnailViewHolder holder = getViewHolderAtPosition(position); if (holder != null) { - holder.mTxtSelectionCount.setSelected(isSelected); - updateSelectionCountForPosition(position, isSelected, holder.mTxtSelectionCount); + holder.mBinding.textSelectionCount.setSelected(isSelected); + updateSelectionCountForPosition(position, isSelected, holder.mBinding.textSelectionCount); if (isSelected) { - AniUtils.scale(holder.mImgThumbnail, SCALE_NORMAL, SCALE_SELECTED, ANI_DURATION); + AniUtils.scale(holder.mBinding.imageThumbnail, SCALE_NORMAL, SCALE_SELECTED, ANI_DURATION); } else { - AniUtils.scale(holder.mImgThumbnail, SCALE_SELECTED, SCALE_NORMAL, ANI_DURATION); + AniUtils.scale(holder.mBinding.imageThumbnail, SCALE_SELECTED, SCALE_NORMAL, ANI_DURATION); } if (canMultiselect()) { - AniUtils.startAnimation(holder.mTxtSelectionCount, R.anim.pop); + AniUtils.startAnimation(holder.mBinding.textSelectionCount, R.anim.pop); } else if (isSelected) { - AniUtils.fadeIn(holder.mTxtSelectionCount, ANI_DURATION); + AniUtils.fadeIn(holder.mBinding.textSelectionCount, ANI_DURATION); } else { - AniUtils.fadeOut(holder.mTxtSelectionCount, ANI_DURATION); + AniUtils.fadeOut(holder.mBinding.textSelectionCount, ANI_DURATION); } } @@ -367,27 +368,19 @@ private void notifySelectionCountChanged() { * ViewHolder containing a device thumbnail */ class ThumbnailViewHolder extends RecyclerView.ViewHolder { - private final ImageView mImgThumbnail; - private final TextView mTxtSelectionCount; - private final ImageView mVideoOverlay; - private final TextView mTxtVideoDuration; - - ThumbnailViewHolder(View view) { - super(view); + PhotoPickerThumbnailBinding mBinding; - mImgThumbnail = view.findViewById(R.id.image_thumbnail); - mTxtSelectionCount = view.findViewById(R.id.text_selection_count); - mVideoOverlay = view.findViewById(R.id.image_video_overlay); - mTxtVideoDuration = view.findViewById(R.id.text_video_duration); - - mImgThumbnail.getLayoutParams().width = mThumbWidth; - mImgThumbnail.getLayoutParams().height = mThumbHeight; + ThumbnailViewHolder(PhotoPickerThumbnailBinding binding) { + super(binding.getRoot()); + this.mBinding = binding; + mBinding.imageThumbnail.getLayoutParams().width = mThumbWidth; + mBinding.imageThumbnail.getLayoutParams().height = mThumbHeight; if (!canMultiselect()) { - mTxtSelectionCount.setBackgroundResource(R.drawable.photo_picker_circle_pressed); + mBinding.textSelectionCount.setBackgroundResource(R.drawable.photo_picker_circle_pressed); } - mImgThumbnail.setOnClickListener(new View.OnClickListener() { + mBinding.imageThumbnail.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = getAdapterPosition(); @@ -406,7 +399,7 @@ public void onClick(View v) { // } // }); - mVideoOverlay.setOnClickListener(new View.OnClickListener() { + mBinding.imageVideoOverlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = getAdapterPosition(); @@ -451,8 +444,8 @@ private long getVideoDuration(Uri videoUri) { try { mediaMetadataRetriever.setDataSource(mContext, videoUri); durationUs = Long.parseLong( - mediaMetadataRetriever.extractMetadata( - MediaMetadataRetriever.METADATA_KEY_DURATION)); + mediaMetadataRetriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_DURATION)); } catch (NumberFormatException e) { durationUs = -1; } catch (Exception ex) { @@ -463,6 +456,7 @@ private long getVideoDuration(Uri videoUri) { return durationUs; } + /* * builds the list of media items from the device */ @@ -504,7 +498,7 @@ private void addMedia(Uri baseUri, boolean isVideo) { String[] projection = {ID_COL}; Cursor cursor = null; try { - cursor = mContext.getContentResolver().query( + cursor = mContext.getContentResolver().query( baseUri, projection, null, diff --git a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java index 3741741c5..90c9a77ec 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java @@ -10,11 +10,9 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.PopupMenu; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -22,19 +20,19 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.view.ActionMode; import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.automattic.loop.photopicker.utils.AniUtils; -import com.automattic.loop.photopicker.PhotoPickerAdapter.PhotoPickerAdapterListener; import com.automattic.loop.R; -import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.automattic.loop.databinding.PhotoPickerFragmentBinding; +import com.automattic.loop.photopicker.PhotoPickerAdapter.PhotoPickerAdapterListener; +import com.automattic.loop.photopicker.utils.AniUtils; +import com.wordpress.stories.ViewBindingFragment; import java.util.ArrayList; import java.util.List; -public class PhotoPickerFragment extends Fragment { +public class PhotoPickerFragment extends ViewBindingFragment { private static final String KEY_LAST_TAPPED_ICON = "last_tapped_icon"; private static final String KEY_SELECTED_POSITIONS = "selected_positions"; @@ -61,20 +59,15 @@ public interface PhotoPickerListener { void onPhotoPickerIconClicked(@NonNull PhotoPickerIcon icon); } - private EmptyViewRecyclerView mRecycler; private PhotoPickerAdapter mAdapter; - private View mBottomBar; - private ActionableEmptyView mSoftAskView; private ActionMode mActionMode; private GridLayoutManager mGridManager; private Parcelable mRestoreState; private PhotoPickerListener mListener; private PhotoPickerIcon mLastTappedIcon; private MediaBrowserType mBrowserType; -// private SiteModel mSite; + // private SiteModel mSite; private ArrayList mSelectedPositions; - private TextView mChooseItemsDescription; - private FloatingActionButton mTakePicture; // public static PhotoPickerFragment newInstance(@NonNull PhotoPickerListener listener, // @NonNull MediaBrowserType browserType, @@ -105,6 +98,12 @@ public static PhotoPickerFragment newInstance(@NonNull PhotoPickerListener liste return fragment; } + @NonNull + @Override + public PhotoPickerFragmentBinding inflateBinding(@NonNull View view) { + return PhotoPickerFragmentBinding.bind(view); + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -121,25 +120,25 @@ public void onCreate(@Nullable Bundle savedInstanceState) { } } + @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.photo_picker_fragment, container, false); + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.photo_picker_fragment, container, false); + } - mTakePicture = view.findViewById(R.id.take_picture); - mTakePicture.setOnClickListener(new OnClickListener() { - @Override public void onClick(View view) { - doIconClicked(PhotoPickerIcon.WP_STORIES_CAPTURE); - } - }); + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); - mRecycler = view.findViewById(R.id.recycler); - mRecycler.setEmptyView(view.findViewById(R.id.actionable_empty_view)); - mRecycler.setHasFixedSize(true); + getBinding().takePicture.setOnClickListener(v -> doIconClicked(PhotoPickerIcon.WP_STORIES_CAPTURE)); + getBinding().recycler.setEmptyView(getBinding().actionableEmptyView); + getBinding().recycler.setHasFixedSize(true); // disable thumbnail loading during a fling to conserve memory final int minDistance = ViewConfiguration.get(getActivity()).getScaledMaximumFlingVelocity() / 2; - mRecycler.setOnFlingListener(new RecyclerView.OnFlingListener() { + getBinding().recycler.setOnFlingListener(new RecyclerView.OnFlingListener() { @Override public boolean onFling(int velocityX, int velocityY) { if (Math.abs(velocityY) > minDistance) { @@ -148,7 +147,7 @@ public boolean onFling(int velocityX, int velocityY) { return false; } }); - mRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() { + getBinding().recycler.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); @@ -158,10 +157,8 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { } }); - mBottomBar = view.findViewById(R.id.bottom_bar); - if (!canShowBottomBar()) { - mBottomBar.setVisibility(View.GONE); + getBinding().bottomBar.setVisibility(View.GONE); } else { // mBottomBar.findViewById(R.id.icon_camera).setOnClickListener(new View.OnClickListener() { // @Override @@ -173,15 +170,12 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { // } // } // }); - mBottomBar.findViewById(R.id.icon_picker).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mBrowserType == MediaBrowserType.GRAVATAR_IMAGE_PICKER - || mBrowserType == MediaBrowserType.SITE_ICON_PICKER) { - doIconClicked(PhotoPickerIcon.ANDROID_CHOOSE_PHOTO); - } else { - showPickerPopupMenu(v); - } + getBinding().iconPicker.setOnClickListener(v -> { + if (mBrowserType == MediaBrowserType.GRAVATAR_IMAGE_PICKER + || mBrowserType == MediaBrowserType.SITE_ICON_PICKER) { + doIconClicked(PhotoPickerIcon.ANDROID_CHOOSE_PHOTO); + } else { + showPickerPopupMenu(v); } }); @@ -198,12 +192,6 @@ public void onClick(View v) { // }); // } } - - mSoftAskView = view.findViewById(R.id.soft_ask_view); - - mChooseItemsDescription = view.findViewById(R.id.text_choose_items_to_add); - - return view; } private boolean canShowBottomBar() { @@ -362,7 +350,7 @@ private void hideBottomBar() { } private boolean isBottomBarShowing() { - return mBottomBar.getVisibility() == View.VISIBLE; + return getBinding().bottomBar.getVisibility() == View.VISIBLE; } private final PhotoPickerAdapterListener mAdapterListener = new PhotoPickerAdapterListener() { @@ -370,13 +358,13 @@ private boolean isBottomBarShowing() { public void onSelectedCountChanged(int count) { if (count == 0) { finishActionMode(); - mTakePicture.show(); + getBinding().takePicture.show(); } else { if (mActionMode == null) { ((AppCompatActivity) getActivity()).startSupportActionMode(new ActionModeCallback()); } updateActionModeTitle(mAdapter.isSelectedSingleItemVideo()); - mTakePicture.hide(); + getBinding().takePicture.hide(); } } @@ -394,9 +382,9 @@ public void onAdapterLoaded(boolean isEmpty) { } if (isEmpty) { - mChooseItemsDescription.setVisibility(View.GONE); + getBinding().textChooseItemsToAdd.setVisibility(View.GONE); } else { - mChooseItemsDescription.setVisibility(View.VISIBLE); + getBinding().textChooseItemsToAdd.setVisibility(View.VISIBLE); } } }; @@ -431,8 +419,8 @@ public void reload() { } mGridManager = new GridLayoutManager(getActivity(), NUM_COLUMNS); - mRecycler.setLayoutManager(mGridManager); - mRecycler.setAdapter(getAdapter()); + getBinding().recycler.setLayoutManager(mGridManager); + getBinding().recycler.setAdapter(getAdapter()); getAdapter().refresh(true); } @@ -513,7 +501,7 @@ public void onDestroyActionMode(ActionMode mode) { mActionMode = null; showBottomBar(); getAdapter().clearSelection(); - mTakePicture.show(); + getBinding().takePicture.show(); } } @@ -623,10 +611,10 @@ private void showSoftAskView(boolean show) { // } // }); // - mSoftAskView.setVisibility(View.VISIBLE); + getBinding().softAskView.setVisibility(View.VISIBLE); // hideBottomBar(); - } else if (mSoftAskView.getVisibility() == View.VISIBLE) { - AniUtils.fadeOut(mSoftAskView, AniUtils.Duration.MEDIUM); + } else if (getBinding().softAskView.getVisibility() == View.VISIBLE) { + AniUtils.fadeOut(getBinding().softAskView, AniUtils.Duration.MEDIUM); // showBottomBar(); } } diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 7778543ae..082eeb1a0 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -10,4 +10,4 @@ android:layout_height="match_parent" android:text="@string/main_placeholder"/> - \ No newline at end of file + diff --git a/stories/build.gradle b/stories/build.gradle index 49580c71a..11a26ee27 100644 --- a/stories/build.gradle +++ b/stories/build.gradle @@ -18,6 +18,10 @@ android { consumerProguardFiles 'consumer-rules.pro' } + viewBinding { + enabled = true + } + buildTypes { release { minifyEnabled false @@ -55,6 +59,8 @@ dependencies { implementation project(path: ':photoeditor') + // lifecycle + implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" // ViewModel and LiveData implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" // LiveData diff --git a/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt b/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt new file mode 100644 index 000000000..c9aa2cfc4 --- /dev/null +++ b/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt @@ -0,0 +1,52 @@ +package com.wordpress.stories + +import android.os.Looper +import android.view.LayoutInflater +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent +import androidx.viewbinding.ViewBinding +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +/** + * Created by Bulent Turkmen(@faranjit) on 5.01.2021. + * + * https://gist.github.com/seanghay/0fd991d7a823815500557fe043b052ce#file-view-binding-ktx-kt + */ +fun AppCompatActivity.viewBinding(initializer: (LayoutInflater) -> T) = + ViewBindingPropertyDelegate(this, initializer) + +class ViewBindingPropertyDelegate( + private val activity: AppCompatActivity, private val initializer: (LayoutInflater) -> T +) : ReadOnlyProperty, LifecycleObserver { + private var binding: T? = null + + init { + activity.lifecycle.addObserver(this) + } + + @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) + @Suppress("Unused") + fun onCreate() { + if (binding == null) { + binding = initializer(activity.layoutInflater) + } + + activity.setContentView(binding?.root!!) + activity.lifecycle.removeObserver(this) + } + + override fun getValue(thisRef: AppCompatActivity, property: KProperty<*>): T { + if (binding == null) { + // This must be on the main thread only + if (Looper.myLooper() != Looper.getMainLooper()) { + throw IllegalThreadStateException("This cannot be called from other threads. It should be on the main thread only.") + } + + binding = initializer(thisRef.layoutInflater) + } + return binding!! + } +} diff --git a/stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt b/stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt new file mode 100644 index 000000000..d7f647e08 --- /dev/null +++ b/stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt @@ -0,0 +1,53 @@ +package com.wordpress.stories + +import android.view.View +import androidx.fragment.app.Fragment +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.viewbinding.ViewBinding +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +/** + * Created by Bulent Turkmen(@faranjit) on 5.01.2021. + * + * https://gist.github.com/Zhuinden/ea3189198938bd16c03db628e084a4fa#file-fragmentviewbindingdelegate-kt + */ +fun Fragment.viewBinding(initializer: (View) -> T) = + FragmentViewBindingDelegate(this, initializer) + +class FragmentViewBindingDelegate( + val fragment: Fragment, + val initializer: (View) -> T +) : ReadOnlyProperty { + private var binding: T? = null + + init { + fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onCreate(owner: LifecycleOwner) { + fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner -> + viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + binding = null + } + }) + } + } + }) + } + + override fun getValue(thisRef: Fragment, property: KProperty<*>): T { + val binding = binding + if (binding != null) { + return binding + } + + val lifecycle = fragment.viewLifecycleOwner.lifecycle + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { + throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.") + } + + return initializer(thisRef.requireView()).also { this.binding = it } + } +} diff --git a/stories/src/main/java/com/wordpress/stories/ViewBindingActivity.kt b/stories/src/main/java/com/wordpress/stories/ViewBindingActivity.kt new file mode 100644 index 000000000..421ee2fa9 --- /dev/null +++ b/stories/src/main/java/com/wordpress/stories/ViewBindingActivity.kt @@ -0,0 +1,24 @@ +package com.wordpress.stories + +import android.view.LayoutInflater +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding + +/** + * Created by Bulent Turkmen(@faranjit) on 4.01.2021. + * + * This class is for Java classes that we can not use Kotlin delegates + * such as @see ViewBindingPropertyDelegate. + */ +abstract class ViewBindingActivity : AppCompatActivity() { + protected val binding: VB by viewBinding { + inflateBinding(it) + } + + /** + * Create binding object with given inflater and returns that object + * @param layoutInflater LayoutInflater + * @return Binding + */ + abstract fun inflateBinding(layoutInflater: LayoutInflater): VB +} diff --git a/stories/src/main/java/com/wordpress/stories/ViewBindingFragment.kt b/stories/src/main/java/com/wordpress/stories/ViewBindingFragment.kt new file mode 100644 index 000000000..de98a04c8 --- /dev/null +++ b/stories/src/main/java/com/wordpress/stories/ViewBindingFragment.kt @@ -0,0 +1,27 @@ +package com.wordpress.stories + +import android.view.View +import androidx.fragment.app.Fragment +import androidx.viewbinding.ViewBinding + +/** + * Created by Bulent Turkmen(@faranjit) on 5.01.2021. + * + * This class is for Java classes that we can not use Kotlin delegates + * such as @see FragmentViewBindingDelegate. + */ +abstract class ViewBindingFragment : Fragment() { + protected val binding by viewBinding { + inflateBinding(it) + } + + /** + * Create binding object with given inflater and returns that object. + * Note that {@link androidx.fragment.app.Fragment.#onCreateView(LayoutInflater, ViewGroup, Bundle)} must be called + * before calling binding. + * + * @param view Root view of fragment + * @return Binding + */ + abstract fun inflateBinding(view: View): VB +} 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 5dbdcf780..9b85f7428 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -41,11 +41,9 @@ import androidx.fragment.app.DialogFragment import androidx.lifecycle.Lifecycle.State.DESTROYED import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider -import com.automattic.photoeditor.text.FontResolver import com.automattic.photoeditor.OnPhotoEditorListener import com.automattic.photoeditor.PhotoEditor import com.automattic.photoeditor.SaveSettings -import com.automattic.photoeditor.text.TextStyler import com.automattic.photoeditor.camera.interfaces.CameraSelection import com.automattic.photoeditor.camera.interfaces.FlashIndicatorState import com.automattic.photoeditor.camera.interfaces.ImageCaptureListener @@ -54,10 +52,12 @@ import com.automattic.photoeditor.camera.interfaces.VideoRecorderFragment.FlashS import com.automattic.photoeditor.state.AuthenticationHeadersInterface import com.automattic.photoeditor.state.BackgroundSurfaceManager import com.automattic.photoeditor.state.BackgroundSurfaceManagerReadyListener -import com.automattic.photoeditor.util.FileUtils -import com.automattic.photoeditor.util.FileUtils.Companion.getLoopFrameFile +import com.automattic.photoeditor.text.FontResolver import com.automattic.photoeditor.text.IdentifiableTypeface import com.automattic.photoeditor.text.IdentifiableTypeface.TypefaceId +import com.automattic.photoeditor.text.TextStyler +import com.automattic.photoeditor.util.FileUtils +import com.automattic.photoeditor.util.FileUtils.Companion.getLoopFrameFile import com.automattic.photoeditor.util.PermissionUtils import com.automattic.photoeditor.views.ViewType import com.automattic.photoeditor.views.ViewType.TEXT @@ -102,14 +102,15 @@ import com.wordpress.stories.compose.story.StoryViewModel.StoryFrameListItemUiSt import com.wordpress.stories.compose.story.StoryViewModelFactory import com.wordpress.stories.compose.text.TextEditorDialogFragment import com.wordpress.stories.compose.text.TextStyleGroupManager +import com.wordpress.stories.databinding.ActivityComposerBinding +import com.wordpress.stories.databinding.ContentComposerBinding 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.getDisplayPixelSize import com.wordpress.stories.util.getStoryIndexFromIntentOrBundle import com.wordpress.stories.util.isVideo -import kotlinx.android.synthetic.main.activity_composer.* -import kotlinx.android.synthetic.main.content_composer.* +import com.wordpress.stories.viewBinding import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -130,9 +131,11 @@ enum class ScreenTouchBlockMode { BLOCK_TOUCH_MODE_NONE, BLOCK_TOUCH_MODE_FULL_SCREEN, // used when saving - user is not allowed to touch anything BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION, // used when in error resolution mode: user needs to take + // action, so we allow them to use the StoryFrameSelector and menu, but no edits on // the Photo Editor canvas are allowed at this stage BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY, // used when errors have been sorted out by the user - no edits allowed, + // but they should be good to upload the Story now BLOCK_TOUCH_MODE_DELETE_SLIDE // Used in delete slide mode, tapping the screen releases the block } @@ -186,6 +189,9 @@ interface StoryDiscardListener { } abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelectorTappedListener { + private val binding by viewBinding(ActivityComposerBinding::inflate) + private lateinit var contentComposerBinding: ContentComposerBinding + private lateinit var photoEditor: PhotoEditor private lateinit var backgroundSurfaceManager: BackgroundSurfaceManager private var currentOriginalCapturedFile: File? = null @@ -254,17 +260,17 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // notifications the host app may have // IMPORTANT: this needs to be the first call in the methods linedup for NotificationIntentLoader frameSaveService.setNotificationErrorBaseId( - it.setupErrorNotificationBaseId() + it.setupErrorNotificationBaseId() ) frameSaveService.setNotificationIntent(it.loadIntentForErrorNotification()) val notificationId = FrameSaveNotifier.getNotificationIdForError( - frameSaveService.getNotificationErrorBaseId(), - storyIndex + frameSaveService.getNotificationErrorBaseId(), + storyIndex ) frameSaveService.setDeleteNotificationPendingIntent( - it.loadPendingIntentForErrorNotificationDeletion(notificationId) + it.loadPendingIntentForErrorNotificationDeletion(notificationId) ) } @@ -297,41 +303,56 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun calculateWorkingArea(): Rect { val location = IntArray(2) - photoEditorView.getLocationOnScreen(location) + contentComposerBinding.photoEditorView.getLocationOnScreen(location) val xCoord = location[0] val yCoord = location[1] - val width = photoEditorView.measuredWidth - val height = photoEditorView.measuredHeight + val width = contentComposerBinding.photoEditorView.measuredWidth + val height = contentComposerBinding.photoEditorView.measuredHeight 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, + yCoord + topAreaHeight, + xCoord + width, + yCoord + height - bottomAreaHeight ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_composer) EventBus.getDefault().register(this) - topControlsBaseTopMargin = getLayoutTopMarginBeforeInset(close_button.layoutParams) - nextButtonBaseTopMargin = getLayoutTopMarginBeforeInset(next_button.layoutParams) - ViewCompat.setOnApplyWindowInsetsListener(compose_loop_frame_layout) { _, insets -> + contentComposerBinding = binding.contentComposer + + topControlsBaseTopMargin = getLayoutTopMarginBeforeInset(contentComposerBinding.closeButton.layoutParams) + nextButtonBaseTopMargin = getLayoutTopMarginBeforeInset(contentComposerBinding.nextButton.layoutParams) + ViewCompat.setOnApplyWindowInsetsListener(binding.composeLoopFrameLayout) { _, insets -> // set insetTop as margin to all controls appearing at the top of the screen - addInsetTopMargin(next_button.layoutParams, nextButtonBaseTopMargin, insets.systemWindowInsetTop) - addInsetTopMargin(close_button.layoutParams, topControlsBaseTopMargin, insets.systemWindowInsetTop) - addInsetTopMargin(control_flash_group.layoutParams, topControlsBaseTopMargin, insets.systemWindowInsetTop) + addInsetTopMargin( + contentComposerBinding.nextButton.layoutParams, + nextButtonBaseTopMargin, + insets.systemWindowInsetTop + ) + addInsetTopMargin( + contentComposerBinding.closeButton.layoutParams, + topControlsBaseTopMargin, + insets.systemWindowInsetTop + ) + addInsetTopMargin( + contentComposerBinding.controlFlashGroup.layoutParams, + topControlsBaseTopMargin, + insets.systemWindowInsetTop + ) bottomNavigationBarMargin = insets.systemWindowInsetBottom workingAreaRect = calculateWorkingArea() photoEditor.updateWorkAreaRect(workingAreaRect) - delete_view.addBottomOffset(bottomNavigationBarMargin) - delete_slide_view.addBottomOffset(bottomNavigationBarMargin) - (bottom_strip_view as StoryFrameSelectorFragment).setBottomOffset(bottomNavigationBarMargin) + contentComposerBinding.deleteView.addBottomOffset(bottomNavigationBarMargin) + contentComposerBinding.deleteSlideView.addBottomOffset(bottomNavigationBarMargin) + (supportFragmentManager.findFragmentById(R.id.bottom_strip_view) as? StoryFrameSelectorFragment)?.setBottomOffset( + bottomNavigationBarMargin + ) insets } @@ -342,12 +363,12 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } workingAreaRect = calculateWorkingArea() - photoEditor = PhotoEditor.Builder(this, photoEditorView) - .setPinchTextScalable(true) // set flag to make text scalable when pinch - .setDeleteView(delete_view) - .setWorkAreaRect(workingAreaRect) - .setAuthenticatitonHeaderInterface(authHeaderInterfaceBridge) - .build() // build photo editor sdk + photoEditor = PhotoEditor.Builder(this, contentComposerBinding.photoEditorView) + .setPinchTextScalable(true) // set flag to make text scalable when pinch + .setDeleteView(contentComposerBinding.deleteView) + .setWorkAreaRect(workingAreaRect) + .setAuthenticatitonHeaderInterface(authHeaderInterfaceBridge) + .build() // build photo editor sdk photoEditor.setOnPhotoEditorListener(object : OnPhotoEditorListener { override fun onEditTextChangeListener( @@ -365,9 +386,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec rootView.visibility = View.INVISIBLE val textEditorDialogFragment = TextEditorDialogFragment.show( - this@ComposeLoopFrameActivity, - text, - textStyler) + this@ComposeLoopFrameActivity, + text, + textStyler + ) textEditorDialogFragment.setOnTextEditorListener(object : TextEditorDialogFragment.TextEditor { override fun onDone(inputText: String, textStyler: TextStyler) { // fixes https://github.com/Automattic/stories-android/issues/453 @@ -397,7 +419,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } override fun onRemoveViewListener(viewType: ViewType, numberOfAddedViews: Int) { - next_button.visibility = View.VISIBLE + contentComposerBinding.nextButton.visibility = View.VISIBLE } override fun onStartViewChangeListener(viewType: ViewType) { @@ -417,7 +439,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } override fun onRemoveViewReadyListener(removedView: View, ready: Boolean) { - delete_view.setReadyForDelete(ready) + contentComposerBinding.deleteView.setReadyForDelete(ready) } override fun getWorkingAreaRect(): Rect? { @@ -432,38 +454,38 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec }) backgroundSurfaceManager = BackgroundSurfaceManager( - savedInstanceState, - lifecycle, - photoEditorView, - supportFragmentManager, - object : FlashSupportChangeListener { - override fun onFlashSupportChanged(isSupported: Boolean) { - if (isSupported) { - camera_flash_button.visibility = View.VISIBLE - label_flash.visibility = View.VISIBLE - } else { - camera_flash_button.visibility = View.INVISIBLE - label_flash.visibility = View.INVISIBLE - } - } - }, - BuildConfig.USE_CAMERAX, - object : BackgroundSurfaceManagerReadyListener { - override fun onBackgroundSurfaceManagerReady() { - if (savedInstanceState == null && !firstIntentLoaded) { - onLoadFromIntent(intent) - firstIntentLoaded = true + savedInstanceState, + lifecycle, + contentComposerBinding.photoEditorView, + supportFragmentManager, + object : FlashSupportChangeListener { + override fun onFlashSupportChanged(isSupported: Boolean) { + if (isSupported) { + contentComposerBinding.cameraFlashButton.visibility = View.VISIBLE + contentComposerBinding.labelFlash.visibility = View.VISIBLE + } else { + contentComposerBinding.cameraFlashButton.visibility = View.INVISIBLE + contentComposerBinding.labelFlash.visibility = View.INVISIBLE + } } + }, + BuildConfig.USE_CAMERAX, + object : BackgroundSurfaceManagerReadyListener { + override fun onBackgroundSurfaceManagerReady() { + if (savedInstanceState == null && !firstIntentLoaded) { + onLoadFromIntent(intent) + firstIntentLoaded = true + } - if (launchCameraRequestPending) { - launchCameraRequestPending = false - launchCameraPreviewWithSurfaceSafeguard() - } else if (launchVideoPlayerRequestPending) { - launchVideoPlayerRequestPending = false - showPlayVideoWithSurfaceSafeguard(launchVideoPlayerRequestPendingSource) + if (launchCameraRequestPending) { + launchCameraRequestPending = false + launchCameraPreviewWithSurfaceSafeguard() + } else if (launchVideoPlayerRequestPending) { + launchVideoPlayerRequestPending = false + showPlayVideoWithSurfaceSafeguard(launchVideoPlayerRequestPendingSource) + } } - } - }, + }, authHeaderInterfaceBridge, useTempCaptureFile ) @@ -488,8 +510,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // before instantiating the ViewModel, we need to get the storyIndexToSelect storyIndexToSelect = getStoryIndexFromIntentOrBundle(savedInstanceState, intent) - storyViewModel = ViewModelProvider(this, - StoryViewModelFactory(StoryRepository, storyIndexToSelect) + storyViewModel = ViewModelProvider( + this, + StoryViewModelFactory(StoryRepository, storyIndexToSelect) )[StoryViewModel::class.java] // request the BackgroundSurfaceManager to prime the textureView so it's ready when needed. @@ -497,11 +520,13 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // check camera selection, flash state from preferences CameraSelection.valueOf( - getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_camera_selection), 0))?.let { + getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_camera_selection), 0) + )?.let { cameraSelection = it } FlashIndicatorState.valueOf( - getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_flash_mode_selection), 0))?.let { + getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_flash_mode_selection), 0) + )?.let { flashModeSelection = it } @@ -511,21 +536,21 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec setupStoryViewModelObservers() if (intent.getBooleanExtra(KEY_STORY_EDIT_MODE, false)) { - next_button.buttonMode = DONE + contentComposerBinding.nextButton.buttonMode = DONE } if (savedInstanceState != null) { currentOriginalCapturedFile = - savedInstanceState.getSerializable(STATE_KEY_CURRENT_ORIGINAL_CAPTURED_FILE) as File? + savedInstanceState.getSerializable(STATE_KEY_CURRENT_ORIGINAL_CAPTURED_FILE) as File? preHookRun = savedInstanceState.getBoolean(STATE_KEY_PREHOOK_RUN) firstIntentLoaded = savedInstanceState.getBoolean(STATE_KEY_FIRST_INTENT_LOADED) permissionsRequestForCameraInProgress = savedInstanceState.getBoolean(STATE_KEY_PERMISSION_REQ_IN_PROGRESS) storyViewModel.loadStory( - StorySerializerUtils.deserializeStory( - requireNotNull(savedInstanceState.getString(STATE_KEY_STORY_SAVE_STATE)) - ) + StorySerializerUtils.deserializeStory( + requireNotNull(savedInstanceState.getString(STATE_KEY_STORY_SAVE_STATE)) + ) ) val selectedFrameIndex = savedInstanceState.getInt(STATE_KEY_STORY_SAVE_STATE_SELECTED_FRAME) @@ -608,8 +633,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun prepareErrorScreen(storySaveResult: StorySaveResult) { // disable the Publish button - need to pass a postDelayed given it somehow doesn't play well right on start // being shown - next_button.postDelayed({ - next_button.isEnabled = false + contentComposerBinding.nextButton.postDelayed({ + contentComposerBinding.nextButton.isEnabled = false }, 500) val errors = storySaveResult.frameSaveResult.filter { it.resultReason is SaveError } @@ -617,8 +642,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // select the first errored frame onStoryFrameSelected( - oldIndex = StoryRepository.DEFAULT_FRAME_NONE_SELECTED, - newIndex = minIndexToSelect!!.frameIndex + oldIndex = StoryRepository.DEFAULT_FRAME_NONE_SELECTED, + newIndex = minIndexToSelect!!.frameIndex ) // show dialog @@ -629,9 +654,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val errorDialogTitle = String.format(stringSingularOrPlural, errors.size) FrameSaveErrorDialog.newInstance( - errorDialogTitle, - getString(R.string.dialog_story_saving_error_message), - getString(android.R.string.ok) + errorDialogTitle, + getString(R.string.dialog_story_saving_error_message), + getString(android.R.string.ok) ).show(supportFragmentManager, FRAGMENT_DIALOG) } @@ -640,22 +665,22 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val intent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) if (intent.resolveActivity(packageManager) != null) { FrameSaveErrorDialog.newInstance( - title = getString(R.string.dialog_insufficient_device_storage_error_title), - message = getString(R.string.dialog_insufficient_device_storage_error_message), - okButtonLabel = getString(R.string.dialog_insufficient_device_storage_error_ok_button), - listener = object : FrameSaveErrorDialogOk { - override fun OnOkClicked(dialog: DialogFragment) { - dialog.dismiss() - val settingsIntent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) - if (settingsIntent.resolveActivity(packageManager) != null) { - startActivity(settingsIntent) + title = getString(R.string.dialog_insufficient_device_storage_error_title), + message = getString(R.string.dialog_insufficient_device_storage_error_message), + okButtonLabel = getString(R.string.dialog_insufficient_device_storage_error_ok_button), + listener = object : FrameSaveErrorDialogOk { + override fun OnOkClicked(dialog: DialogFragment) { + dialog.dismiss() + val settingsIntent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) + if (settingsIntent.resolveActivity(packageManager) != null) { + startActivity(settingsIntent) + } } - } - }).show(supportFragmentManager, FRAGMENT_DIALOG) + }).show(supportFragmentManager, FRAGMENT_DIALOG) } else { FrameSaveErrorDialog.newInstance( - title = getString(R.string.dialog_insufficient_device_storage_error_title), - message = getString(R.string.dialog_insufficient_device_storage_error_message) + title = getString(R.string.dialog_insufficient_device_storage_error_title), + message = getString(R.string.dialog_insufficient_device_storage_error_message) ).show(supportFragmentManager, FRAGMENT_DIALOG) } } @@ -728,7 +753,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec super.onResume() // Before setting full screen flags, we must wait a bit to let UI settle; otherwise, we may // be trying to set app to immersive mode before it's ready and the flags do not stick - photoEditorView.postDelayed({ + contentComposerBinding.photoEditorView.postDelayed({ hideStatusBar(window) workingAreaRect = calculateWorkingArea() photoEditor.updateWorkAreaRect(workingAreaRect) @@ -745,9 +770,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // save Story slide (frame) state addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) - outState.putString(STATE_KEY_STORY_SAVE_STATE, StorySerializerUtils.serializeStory( + outState.putString( + STATE_KEY_STORY_SAVE_STATE, StorySerializerUtils.serializeStory( storyViewModel.getStoryAtIndex(storyViewModel.getCurrentStoryIndex()) - ) + ) ) outState.putInt(STATE_KEY_STORY_SAVE_STATE_SELECTED_FRAME, storyViewModel.getSelectedFrameIndex()) super.onSaveInstanceState(outState) @@ -777,7 +803,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec override fun onBackPressed() { if (!backgroundSurfaceManager.cameraVisible()) { - close_button.performClick() + contentComposerBinding.closeButton.performClick() } else if (storyViewModel.getCurrentStorySize() > 0) { showCurrentSelectedFrame() } else { @@ -804,7 +830,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val providerHandlesMediaPickerResult = mediaPickerProvider?.providerHandlesOnActivityResult() ?: false if (data.hasExtra(requestCodes.EXTRA_MEDIA_URIS) && !providerHandlesMediaPickerResult) { val uriList: List = convertStringArrayIntoUrisList( - data.getStringArrayExtra(requestCodes.EXTRA_MEDIA_URIS) + data.getStringArrayExtra(requestCodes.EXTRA_MEDIA_URIS) ) addFramesToStoryFromMediaUriList(uriList) setDefaultSelectionAndUpdateBackgroundSurfaceUI(uriList) @@ -846,10 +872,12 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec protected fun addFrameToStoryFromMediaUri(mediaUri: Uri) { storyViewModel - .addStoryFrameItemToCurrentStory(StoryFrameItem( - UriBackgroundSource(contentUri = mediaUri), - frameItemType = if (isVideo(mediaUri.toString())) VIDEO() else IMAGE - )) + .addStoryFrameItemToCurrentStory( + StoryFrameItem( + UriBackgroundSource(contentUri = mediaUri), + frameItemType = if (isVideo(mediaUri.toString())) VIDEO() else IMAGE + ) + ) } private fun updateBackgroundSurfaceUIWithStoryFrame( @@ -860,185 +888,191 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun addClickListeners() { - camera_capture_button - .setOnTouchListener( - PressAndHoldGestureHelper( - PressAndHoldGestureHelper.CLICK_LENGTH, - object : PressAndHoldGestureListener { - override fun onClickGesture() { - if (cameraOperationInCourse) { - showToast(getString(R.string.toast_capture_operation_in_progress)) - return - } - timesUpHandler.removeCallbacksAndMessages(null) - takeStillPicture() - } - override fun onHoldingGestureStart() { - timesUpHandler.removeCallbacksAndMessages(null) - if (PermissionUtils.allVideoPermissionsGranted(this@ComposeLoopFrameActivity)) { - // if we at least have - startRecordingVideoAfterVibrationIndication() - } else { - // request permissions including audio for video - val permissionName = PermissionUtils.anyVideoNeededPermissionPermanentlyDenied( - this@ComposeLoopFrameActivity - ) - - permissionName?.let { - showPermissionPermanentlyDeniedDialog(it) - } ?: PermissionUtils.requestAllRequiredPermissionsIncludingAudioForVideo( - this@ComposeLoopFrameActivity - ).also { - permissionsRequestForCameraInProgress = true - } - } - } + contentComposerBinding.run { + cameraCaptureButton + .setOnTouchListener( + PressAndHoldGestureHelper( + PressAndHoldGestureHelper.CLICK_LENGTH, + object : PressAndHoldGestureListener { + override fun onClickGesture() { + if (cameraOperationInCourse) { + showToast(getString(R.string.toast_capture_operation_in_progress)) + return + } + timesUpHandler.removeCallbacksAndMessages(null) + takeStillPicture() + } + + override fun onHoldingGestureStart() { + timesUpHandler.removeCallbacksAndMessages(null) + if (PermissionUtils.allVideoPermissionsGranted(this@ComposeLoopFrameActivity)) { + // if we at least have + startRecordingVideoAfterVibrationIndication() + } else { + // request permissions including audio for video + val permissionName = PermissionUtils.anyVideoNeededPermissionPermanentlyDenied( + this@ComposeLoopFrameActivity + ) + + permissionName?.let { + showPermissionPermanentlyDeniedDialog(it) + } + ?: PermissionUtils.requestAllRequiredPermissionsIncludingAudioForVideo( + this@ComposeLoopFrameActivity + ).also { + permissionsRequestForCameraInProgress = true + } + } + } + + override fun onHoldingGestureEnd() { + stopRecordingVideo(false) + } + + override fun onHoldingGestureCanceled() { + stopRecordingVideo(true) + } + + override fun onStartDetectionWait() { + if (cameraOperationInCourse) { + showToast(getString(R.string.toast_capture_operation_in_progress)) + return + } + // when the wait to see whether this is a "press and hold" gesture starts, + // start the animation to grow the capture button radius + cameraCaptureButton + .animate() + .scaleXBy(0.3f) // scale up by 30% + .scaleYBy(0.3f) + .duration = PressAndHoldGestureHelper.CLICK_LENGTH + } + + override fun onTouchEventDetectionEnd() { + if (cameraOperationInCourse) { + return + } + // when gesture detection ends, we're good to + // get the capture button shape as it originally was (idle state) + cameraCaptureButton.clearAnimation() + cameraCaptureButton + .animate() + .scaleX(1.0f) + .scaleY(1.0f) + .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 + } + }) + ) - override fun onHoldingGestureEnd() { - stopRecordingVideo(false) - } + containerGalleryUpload.setOnClickListener { + showMediaPicker() + } - override fun onHoldingGestureCanceled() { - stopRecordingVideo(true) - } + cameraFlipGroup.setOnClickListener { + cameraSelection = backgroundSurfaceManager.flipCamera() + saveCameraSelectionPref() + } - override fun onStartDetectionWait() { - if (cameraOperationInCourse) { - showToast(getString(R.string.toast_capture_operation_in_progress)) - return - } - // when the wait to see whether this is a "press and hold" gesture starts, - // start the animation to grow the capture button radius - camera_capture_button - .animate() - .scaleXBy(0.3f) // scale up by 30% - .scaleYBy(0.3f) - .duration = PressAndHoldGestureHelper.CLICK_LENGTH - } + // attach listener a bit delayed as we need to have cameraBasicHandling created first + photoEditorView.postDelayed({ + cameraFlashGroup.setAllOnClickListener(OnClickListener { + flashModeSelection = backgroundSurfaceManager.switchFlashState() + updateFlashModeSelectionIcon() + saveFlashModeSelectionPref() + }) + }, SURFACE_MANAGER_READY_LAUNCH_DELAY) - override fun onTouchEventDetectionEnd() { - if (cameraOperationInCourse) { - return - } - // when gesture detection ends, we're good to - // get the capture button shape as it originally was (idle state) - camera_capture_button.clearAnimation() - camera_capture_button - .animate() - .scaleX(1.0f) - .scaleY(1.0f) - .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 + closeButton.setOnClickListener { + when { + backgroundSurfaceManager.cameraVisible() -> { + onBackPressed() + } + !backgroundSurfaceManager.cameraVisible() -> { + // Show discard dialog + var discardTitle = getString(R.string.dialog_discard_story_title) + var discardMessage = getString(R.string.dialog_discard_story_message) + var discardOkButton = getString(R.string.dialog_discard_story_ok_button) + if (intent.getBooleanExtra(KEY_STORY_EDIT_MODE, false)) { + discardTitle = getString(R.string.dialog_discard_story_title_edit) + discardMessage = getString(R.string.dialog_discard_story_message_edit) + discardOkButton = getString(R.string.dialog_discard_story_ok_button_edit) } - }) - ) - - container_gallery_upload.setOnClickListener { - showMediaPicker() - } - - camera_flip_group.setOnClickListener { - cameraSelection = backgroundSurfaceManager.flipCamera() - saveCameraSelectionPref() - } - - // attach listener a bit delayed as we need to have cameraBasicHandling created first - photoEditorView.postDelayed({ - camera_flash_group.setAllOnClickListener(OnClickListener { - flashModeSelection = backgroundSurfaceManager.switchFlashState() - updateFlashModeSelectionIcon() - saveFlashModeSelectionPref() - }) - }, SURFACE_MANAGER_READY_LAUNCH_DELAY) - - close_button.setOnClickListener { - when { - backgroundSurfaceManager.cameraVisible() -> { - onBackPressed() - } - !backgroundSurfaceManager.cameraVisible() -> { - // Show discard dialog - var discardTitle = getString(R.string.dialog_discard_story_title) - var discardMessage = getString(R.string.dialog_discard_story_message) - var discardOkButton = getString(R.string.dialog_discard_story_ok_button) - if (intent.getBooleanExtra(KEY_STORY_EDIT_MODE, false)) { - discardTitle = getString(R.string.dialog_discard_story_title_edit) - discardMessage = getString(R.string.dialog_discard_story_message_edit) - discardOkButton = getString(R.string.dialog_discard_story_ok_button_edit) + FrameSaveErrorDialog.newInstance( + title = discardTitle, + message = discardMessage, + okButtonLabel = discardOkButton, + listener = object : FrameSaveErrorDialogOk { + override fun OnOkClicked(dialog: DialogFragment) { + addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) + dialog.dismiss() + // discard the whole story + safelyDiscardCurrentStoryAndCleanUpIntent() + storyDiscardListener?.onStoryDiscarded() + } + }).show(supportFragmentManager, FRAGMENT_DIALOG) } - FrameSaveErrorDialog.newInstance( - title = discardTitle, - message = discardMessage, - okButtonLabel = discardOkButton, - listener = object : FrameSaveErrorDialogOk { - override fun OnOkClicked(dialog: DialogFragment) { - addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) - dialog.dismiss() - // discard the whole story - safelyDiscardCurrentStoryAndCleanUpIntent() - storyDiscardListener?.onStoryDiscarded() - } - }).show(supportFragmentManager, FRAGMENT_DIALOG) } } - } - sound_button.setOnClickListener { - // flip the flag on audio muted on the VM, the change will get propagated to the UI - storyViewModel.flipCurrentSelectedFrameOnAudioMuted() - } - - text_add_button.setOnClickListener { - addNewText() - } + soundButton.setOnClickListener { + // flip the flag on audio muted on the VM, the change will get propagated to the UI + storyViewModel.flipCurrentSelectedFrameOnAudioMuted() + } - stickers_add_button.setOnClickListener { - // avoid multiple clicks when the one click is already being processed, fixes - // https://github.com/Automattic/stories-android/issues/455 - if (!emojiPickerFragment.isAdded && !emojiPickerFragment.isVisible) { - emojiPickerFragment.show(supportFragmentManager, emojiPickerFragment.tag) + textAddButton.setOnClickListener { + addNewText() } - } - next_button.setOnClickListener { - prepublishingEventProvider?.onStorySaveButtonPressed() - } + stickersAddButton.setOnClickListener { + // avoid multiple clicks when the one click is already being processed, fixes + // https://github.com/Automattic/stories-android/issues/455 + if (!emojiPickerFragment.isAdded && !emojiPickerFragment.isVisible) { + emojiPickerFragment.show(supportFragmentManager, emojiPickerFragment.tag) + } + } - retry_button.setOnClickListener { - // trigger the Service again, for this frame only - storyFrameIndexToRetry = storyViewModel.getSelectedFrameIndex() - retry_button.setSaving(true) - saveStory() - } + nextButton.setOnClickListener { + prepublishingEventProvider?.onStorySaveButtonPressed() + } - delete_slide_view.setOnClickListener { - var messageToUse = getString(R.string.dialog_discard_page_message) - if (storyViewModel.getSelectedFrame()?.saveResultReason != SaveSuccess) { - messageToUse = getString(R.string.dialog_discard_errored_page_message) + retryButton.setOnClickListener { + // trigger the Service again, for this frame only + storyFrameIndexToRetry = storyViewModel.getSelectedFrameIndex() + retryButton.setSaving(true) + saveStory() } - // show dialog - FrameSaveErrorDialog.newInstance( - title = getString(R.string.dialog_discard_page_title), - message = messageToUse, - okButtonLabel = getString(R.string.dialog_discard_page_ok_button), - listener = object : FrameSaveErrorDialogOk { - override fun OnOkClicked(dialog: DialogFragment) { - dialog.dismiss() - if (storyViewModel.getCurrentStorySize() == 1) { - // discard the whole story - safelyDiscardCurrentStoryAndCleanUpIntent() - storyDiscardListener?.onStoryDiscarded() - } else { - // get currentFrame value as it will change after calling onAboutToDeleteStoryFrame - val currentFrameToDeleteIndex = storyViewModel.getSelectedFrameIndex() - onAboutToDeleteStoryFrame(currentFrameToDeleteIndex) - storyDiscardListener?.onFrameRemove(storyViewModel.getCurrentStoryIndex(), - currentFrameToDeleteIndex) - // now discard it from the viewModel - storyViewModel.removeFrameAt(currentFrameToDeleteIndex) + + deleteSlideView.setOnClickListener { + var messageToUse = getString(R.string.dialog_discard_page_message) + if (storyViewModel.getSelectedFrame()?.saveResultReason != SaveSuccess) { + messageToUse = getString(R.string.dialog_discard_errored_page_message) + } + // show dialog + FrameSaveErrorDialog.newInstance( + title = getString(R.string.dialog_discard_page_title), + message = messageToUse, + okButtonLabel = getString(R.string.dialog_discard_page_ok_button), + listener = object : FrameSaveErrorDialogOk { + override fun OnOkClicked(dialog: DialogFragment) { + dialog.dismiss() + if (storyViewModel.getCurrentStorySize() == 1) { + // discard the whole story + safelyDiscardCurrentStoryAndCleanUpIntent() + storyDiscardListener?.onStoryDiscarded() + } else { + // get currentFrame value as it will change after calling onAboutToDeleteStoryFrame + val currentFrameToDeleteIndex = storyViewModel.getSelectedFrameIndex() + onAboutToDeleteStoryFrame(currentFrameToDeleteIndex) + storyDiscardListener?.onFrameRemove( + storyViewModel.getCurrentStoryIndex(), + currentFrameToDeleteIndex + ) + // now discard it from the viewModel + storyViewModel.removeFrameAt(currentFrameToDeleteIndex) + } } - } - }).show(supportFragmentManager, FRAGMENT_DIALOG) - disableDeleteSlideMode() + }).show(supportFragmentManager, FRAGMENT_DIALOG) + disableDeleteSlideMode() + } } } @@ -1066,7 +1100,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun anyOfOriginalIntentResultsIsError(): Boolean { if (intent.hasExtra(KEY_STORY_SAVE_RESULT)) { val storySaveResult = - intent.getParcelableExtra(KEY_STORY_SAVE_RESULT) as StorySaveResult? + intent.getParcelableExtra(KEY_STORY_SAVE_RESULT) as StorySaveResult? storySaveResult?.let { // where there any errors when we opened the Activity to handle those errors? return storySaveResult.isSuccess() @@ -1108,26 +1142,26 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // 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) - transition = photoEditorView.getLayoutTransition() - photoEditorView.layoutTransition = null + transition = contentComposerBinding.photoEditorView.layoutTransition + contentComposerBinding.photoEditorView.layoutTransition = null preHookRun = true } private fun saveStoryPostHook(result: StorySaveResult) { doUnbindService() // re-enable layout change animations - photoEditorView.layoutTransition = transition + contentComposerBinding.photoEditorView.layoutTransition = transition // do this if we are retrying to save the current frame if (storyFrameIndexToRetry != StoryRepository.DEFAULT_NONE_SELECTED) { - retry_button.showSavedAnimation(Runnable { + contentComposerBinding.retryButton.showSavedAnimation(Runnable { if (result.isSuccess()) { hideRetryButton() } else { checkForLowSpaceAndShowDialog() } storyViewModel.updateCurrentSelectedFrameOnRetryResult( - result.frameSaveResult[0] + result.frameSaveResult[0] ) // need to do this so AddedViews get properly placed on the PhotoEditor refreshStoryFrameSelection() @@ -1146,16 +1180,20 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun hideRetryButton() { - retry_button.visibility = View.GONE - // we need this force call given some timing issue when resetting the layout - retry_button.invalidate() + contentComposerBinding.run { + retryButton.visibility = View.GONE + // we need this force call given some timing issue when resetting the layout + retryButton.invalidate() + } } private fun showRetryButton() { - retry_button.setSaving(false) - retry_button.visibility = View.VISIBLE - // we need this force call given some timing issue when resetting the layout - retry_button.invalidate() + contentComposerBinding.run { + retryButton.setSaving(false) + retryButton.visibility = View.VISIBLE + // we need this force call given some timing issue when resetting the layout + retryButton.invalidate() + } } private fun refreshStoryFrameSelection() { @@ -1290,36 +1328,42 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } cameraOperationInCourse = true - camera_capture_button.startProgressingAnimation(CAMERA_STILL_PICTURE_ANIM_MS) - backgroundSurfaceManager.takePicture(object : ImageCaptureListener { - override fun onImageSaved(file: File) { - runOnUiThread { - Glide.with(this@ComposeLoopFrameActivity) - .load(file) - .transform(CenterCrop(), RoundedCorners(16)) - .into(gallery_upload_img) - Glide.with(this@ComposeLoopFrameActivity) - .load(file) - .transform(CenterCrop()) - .into(photoEditorView.source) - storyViewModel.apply { - addStoryFrameItemToCurrentStory( - StoryFrameItem(FileBackgroundSource(file = file), frameItemType = StoryFrameItemType.IMAGE) - ) - setSelectedFrame(storyViewModel.getLastFrameIndexInCurrentStory()) + contentComposerBinding.run { + cameraCaptureButton.startProgressingAnimation(CAMERA_STILL_PICTURE_ANIM_MS) + backgroundSurfaceManager.takePicture(object : ImageCaptureListener { + override fun onImageSaved(file: File) { + runOnUiThread { + Glide.with(this@ComposeLoopFrameActivity) + .load(file) + .transform(CenterCrop(), RoundedCorners(16)) + .into(galleryUploadImg) + Glide.with(this@ComposeLoopFrameActivity) + .load(file) + .transform(CenterCrop()) + .into(photoEditorView.source) + storyViewModel.apply { + addStoryFrameItemToCurrentStory( + StoryFrameItem( + FileBackgroundSource(file = file), + frameItemType = StoryFrameItemType.IMAGE + ) + ) + setSelectedFrame(storyViewModel.getLastFrameIndexInCurrentStory()) + } + showStaticBackground() + currentOriginalCapturedFile = file + waitToReenableCapture() } - showStaticBackground() - currentOriginalCapturedFile = file - waitToReenableCapture() } - } - override fun onError(message: String, cause: Throwable?) { - runOnUiThread { - showToast(getString(R.string.toast_error_saving_image)) - waitToReenableCapture() + + override fun onError(message: String, cause: Throwable?) { + runOnUiThread { + showToast(getString(R.string.toast_error_saving_image)) + waitToReenableCapture() + } } - } - }) + }) + } } private fun startRecordingVideoAfterVibrationIndication() { @@ -1343,15 +1387,19 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // force stop recording video after maximum time limit reached timesUpHandler.postDelayed(timesUpRunnable, CAMERA_VIDEO_RECORD_MAX_LENGTH_MS) // strat progressing animation - camera_capture_button.startProgressingAnimation(CAMERA_VIDEO_RECORD_MAX_LENGTH_MS) + contentComposerBinding.cameraCaptureButton.startProgressingAnimation(CAMERA_VIDEO_RECORD_MAX_LENGTH_MS) backgroundSurfaceManager.startRecordingVideo(object : VideoRecorderFinished { override fun onVideoSaved(file: File?) { currentOriginalCapturedFile = file file?.let { runOnUiThread { storyViewModel.apply { - addStoryFrameItemToCurrentStory(StoryFrameItem(FileBackgroundSource(file = it), - frameItemType = VIDEO())) + addStoryFrameItemToCurrentStory( + StoryFrameItem( + FileBackgroundSource(file = it), + frameItemType = VIDEO() + ) + ) setSelectedFrame(storyViewModel.getLastFrameIndexInCurrentStory()) } } @@ -1376,8 +1424,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator // Vibrate for 100 milliseconds if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - vibrator.vibrate(VibrationEffect.createOneShot( - VIBRATION_INDICATION_LENGTH_MS, VibrationEffect.DEFAULT_AMPLITUDE)) + vibrator.vibrate( + VibrationEffect.createOneShot( + VIBRATION_INDICATION_LENGTH_MS, VibrationEffect.DEFAULT_AMPLITUDE + ) + ) } else { // deprecated in API 26 @Suppress("DEPRECATION") @@ -1392,15 +1443,17 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } if (backgroundSurfaceManager.cameraRecording()) { - camera_capture_button.stopProgressingAnimation() - camera_capture_button.clearAnimation() - camera_capture_button - .animate() - .scaleX(1.0f) - .scaleY(1.0f) - .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 - backgroundSurfaceManager.stopRecordingVideo() - showVideoUIControls() + contentComposerBinding.run { + cameraCaptureButton.stopProgressingAnimation() + cameraCaptureButton.clearAnimation() + cameraCaptureButton + .animate() + .scaleX(1.0f) + .scaleY(1.0f) + .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 + backgroundSurfaceManager.stopRecordingVideo() + showVideoUIControls() + } } } @@ -1420,44 +1473,44 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec file.createNewFile() photoEditor.saveVideoAsFile( - inputFile, - file.absolutePath, - storyViewModel.isSelectedFrameAudioMuted(), - object : PhotoEditor.OnSaveWithCancelAndProgressListener { - override fun onProgress(progress: Double) { - // TODO implement progress - } + inputFile, + file.absolutePath, + storyViewModel.isSelectedFrameAudioMuted(), + object : PhotoEditor.OnSaveWithCancelAndProgressListener { + override fun onProgress(progress: Double) { + // TODO implement progress + } - override fun onCancel(noAddedViews: Boolean) { - runOnUiThread { - hideLoading() - showSnackbar("No views added - original video saved") + override fun onCancel(noAddedViews: Boolean) { + runOnUiThread { + hideLoading() + showSnackbar("No views added - original video saved") + } } - } - override fun onSuccess(filePath: String) { - runOnUiThread { - hideLoading() - deleteCapturedMedia() - photoEditor.clearAllViews() - sendNewStoryFrameReadyBroadcast(file) - showSnackbar( - getString(R.string.label_snackbar_loop_frame_saved), - getString(R.string.label_snackbar_share), - OnClickListener { shareAction(file) } - ) - hideEditModeUIControls() - switchCameraPreviewOn() + override fun onSuccess(filePath: String) { + runOnUiThread { + hideLoading() + deleteCapturedMedia() + photoEditor.clearAllViews() + sendNewStoryFrameReadyBroadcast(file) + showSnackbar( + getString(R.string.label_snackbar_loop_frame_saved), + getString(R.string.label_snackbar_share), + OnClickListener { shareAction(file) } + ) + hideEditModeUIControls() + switchCameraPreviewOn() + } } - } - override fun onFailure(exception: Exception) { - runOnUiThread { - hideLoading() - showSnackbar("Failed to save Video") + override fun onFailure(exception: Exception) { + runOnUiThread { + hideLoading() + showSnackbar("Failed to save Video") + } } - } - }) + }) } catch (e: IOException) { e.printStackTrace() hideLoading() @@ -1477,30 +1530,30 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec file.createNewFile() val saveSettings = SaveSettings.Builder() - .setClearViewsEnabled(true) - .setTransparencyEnabled(true) - .build() + .setClearViewsEnabled(true) + .setTransparencyEnabled(true) + .build() photoEditor.saveVideoFromStaticBackgroundAsFile( - file.absolutePath, - saveSettings, - object : PhotoEditor.OnSaveWithCancelListener { - override fun onCancel(noAddedViews: Boolean) { - // TODO not implemented - } + file.absolutePath, + saveSettings, + object : PhotoEditor.OnSaveWithCancelListener { + override fun onCancel(noAddedViews: Boolean) { + // TODO not implemented + } - override fun onSuccess(filePath: String) { - // now save the video with emoji, but using the previously saved video as input - hideLoading() - saveVideo(Uri.parse(filePath)) - // TODO: delete the temporal video produced originally - } + override fun onSuccess(filePath: String) { + // now save the video with emoji, but using the previously saved video as input + hideLoading() + saveVideo(Uri.parse(filePath)) + // TODO: delete the temporal video produced originally + } - override fun onFailure(exception: Exception) { - hideLoading() - showSnackbar("Failed to save Video") - } - }) + override fun onFailure(exception: Exception) { + hideLoading() + showSnackbar("Failed to save Video") + } + }) } catch (e: IOException) { e.printStackTrace() hideLoading() @@ -1541,52 +1594,57 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun hideVideoUIControls() { - camera_flash_button.visibility = View.INVISIBLE - label_flash.visibility = View.INVISIBLE - - camera_flip_group.visibility = View.INVISIBLE - - container_gallery_upload.visibility = View.INVISIBLE + contentComposerBinding.run { + cameraFlashButton.visibility = View.INVISIBLE + labelFlash.visibility = View.INVISIBLE + cameraFlipGroup.visibility = View.INVISIBLE + containerGalleryUpload.visibility = View.INVISIBLE + } } private fun showVideoUIControls() { - camera_flash_button.visibility = View.VISIBLE - label_flash.visibility = View.VISIBLE - - camera_flip_group.visibility = View.VISIBLE - - container_gallery_upload.visibility = View.VISIBLE + contentComposerBinding.run { + cameraFlashButton.visibility = View.VISIBLE + labelFlash.visibility = View.VISIBLE + cameraFlipGroup.visibility = View.VISIBLE + containerGalleryUpload.visibility = View.VISIBLE + } } private fun showEditModeUIControls() { - // hide capturing mode controls - hideVideoUIControls() - camera_capture_button.visibility = View.INVISIBLE + contentComposerBinding.run { + // hide capturing mode controls + hideVideoUIControls() + cameraCaptureButton.visibility = View.INVISIBLE - // show proper edit mode controls - updateEditMode() - next_button.visibility = View.VISIBLE - close_button.visibility = View.VISIBLE - delete_slide_view.visibility = View.GONE + // show proper edit mode controls + updateEditMode() + nextButton.visibility = View.VISIBLE + closeButton.visibility = View.VISIBLE + deleteSlideView.visibility = View.GONE + } } private fun hideStoryFrameSelector() { - (bottom_strip_view as StoryFrameSelectorFragment).hide() + (supportFragmentManager.findFragmentById(R.id.bottom_strip_view) as? StoryFrameSelectorFragment)?.hide() } private fun showStoryFrameSelector() { - (bottom_strip_view as StoryFrameSelectorFragment).show() + (supportFragmentManager.findFragmentById(R.id.bottom_strip_view) as? StoryFrameSelectorFragment)?.show() } private fun hideEditModeUIControls() { - camera_capture_button.visibility = View.VISIBLE - - // hide proper edit mode controls - edit_mode_controls.visibility = View.INVISIBLE - sound_button.visibility = View.INVISIBLE - next_button.visibility = View.INVISIBLE - retry_button.visibility = View.GONE - // show capturing mode controls + contentComposerBinding.run { + cameraCaptureButton.visibility = View.VISIBLE + + // hide proper edit mode controls + editModeControls.visibility = View.INVISIBLE + soundButton.visibility = View.INVISIBLE + nextButton.visibility = View.INVISIBLE + retryButton.visibility = View.GONE + // show capturing mode controls + } + showVideoUIControls() } @@ -1595,33 +1653,35 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec hideCloseButton: Boolean = false, hideFrameSelector: Boolean = true ) { - // momentarily hide proper edit mode controls - edit_mode_controls.visibility = View.INVISIBLE - sound_button.visibility = View.INVISIBLE - if (hideFrameSelector) { - hideStoryFrameSelector() - } - if (hideNextButton) { - next_button.visibility = View.INVISIBLE - } - if (hideCloseButton) { - close_button.visibility = View.INVISIBLE + contentComposerBinding.run { + // momentarily hide proper edit mode controls + editModeControls.visibility = View.INVISIBLE + soundButton.visibility = View.INVISIBLE + if (hideFrameSelector) { + hideStoryFrameSelector() + } + if (hideNextButton) { + nextButton.visibility = View.INVISIBLE + } + if (hideCloseButton) { + closeButton.visibility = View.INVISIBLE + } } } private fun updateSoundControl() { if (storyViewModel.getSelectedFrame()?.frameItemType is VIDEO) { - sound_button.visibility = View.VISIBLE + contentComposerBinding.soundButton.visibility = View.VISIBLE if (!storyViewModel.isSelectedFrameAudioMuted()) { backgroundSurfaceManager.videoPlayerUnmute() - sound_button.setImageResource(R.drawable.ic_volume_up_black_24dp) + contentComposerBinding.soundButton.setImageResource(R.drawable.ic_volume_up_black_24dp) } else { backgroundSurfaceManager.videoPlayerMute() - sound_button.setImageResource(R.drawable.ic_volume_off_black_24dp) + contentComposerBinding.soundButton.setImageResource(R.drawable.ic_volume_off_black_24dp) } } else { // images don't have audio - sound_button.visibility = View.INVISIBLE + contentComposerBinding.soundButton.visibility = View.INVISIBLE } } @@ -1629,29 +1689,32 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val originallyErrored = anyOfOriginalIntentResultsIsError() val currentlyErrored = storyViewModel.anyOfCurrentStoryFramesIsErrored() - when { - // if we were in an error-handling situation but now all pages are OK we're ready to go - // don't allow editing or adding new frames but do allow publishing the Story - originallyErrored && !currentlyErrored -> { - blockTouchOnPhotoEditor(BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY) - edit_mode_controls.visibility = View.INVISIBLE - sound_button.visibility = View.INVISIBLE - next_button.isEnabled = true - (bottom_strip_view as StoryFrameSelectorFragment).hideAddFrameControl() - } - currentlyErrored -> { - blockTouchOnPhotoEditor(BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION) - edit_mode_controls.visibility = View.INVISIBLE - sound_button.visibility = View.INVISIBLE - next_button.isEnabled = false - (bottom_strip_view as StoryFrameSelectorFragment).hideAddFrameControl() - } - else -> { // no errors here! this is the normal creation situation: release touch block, enable editing - releaseTouchOnPhotoEditor(BLOCK_TOUCH_MODE_NONE) - edit_mode_controls.visibility = View.VISIBLE - updateSoundControl() - next_button.isEnabled = true - (bottom_strip_view as StoryFrameSelectorFragment).showAddFrameControl() + val storyFrameSelectorFragment = supportFragmentManager.findFragmentById(R.id.bottom_strip_view) as? StoryFrameSelectorFragment + contentComposerBinding.run { + when { + // if we were in an error-handling situation but now all pages are OK we're ready to go + // don't allow editing or adding new frames but do allow publishing the Story + originallyErrored && !currentlyErrored -> { + blockTouchOnPhotoEditor(BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY) + editModeControls.visibility = View.INVISIBLE + soundButton.visibility = View.INVISIBLE + nextButton.isEnabled = true + storyFrameSelectorFragment?.hideAddFrameControl() + } + currentlyErrored -> { + blockTouchOnPhotoEditor(BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION) + editModeControls.visibility = View.INVISIBLE + soundButton.visibility = View.INVISIBLE + nextButton.isEnabled = false + storyFrameSelectorFragment?.hideAddFrameControl() + } + else -> { // no errors here! this is the normal creation situation: release touch block, enable editing + releaseTouchOnPhotoEditor(BLOCK_TOUCH_MODE_NONE) + editModeControls.visibility = View.VISIBLE + updateSoundControl() + nextButton.isEnabled = true + storyFrameSelectorFragment?.showAddFrameControl() + } } } } @@ -1659,8 +1722,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun editModeRestoreAllUIControls() { // show all edit mode controls updateEditMode() - next_button.visibility = View.VISIBLE - close_button.visibility = View.VISIBLE + contentComposerBinding.nextButton.visibility = View.VISIBLE + contentComposerBinding.closeButton.visibility = View.VISIBLE showStoryFrameSelector() } @@ -1678,11 +1741,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun updateFlashModeSelectionIcon() { when (flashModeSelection) { FlashIndicatorState.AUTO -> - camera_flash_button.background = getDrawable(R.drawable.ic_flash_auto_black_24dp) + contentComposerBinding.cameraFlashButton.background = getDrawable(R.drawable.ic_flash_auto_black_24dp) FlashIndicatorState.ON -> - camera_flash_button.background = getDrawable(R.drawable.ic_flash_on_black_24dp) + contentComposerBinding.cameraFlashButton.background = getDrawable(R.drawable.ic_flash_on_black_24dp) FlashIndicatorState.OFF -> - camera_flash_button.background = getDrawable(R.drawable.ic_flash_off_black_24dp) + contentComposerBinding.cameraFlashButton.background = getDrawable(R.drawable.ic_flash_off_black_24dp) } } @@ -1704,8 +1767,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun shareAction(mediaFile: File) { val apkURI = FileProvider.getUriForFile( - this, - applicationContext.packageName + ".provider", mediaFile) + this, + applicationContext.packageName + ".provider", mediaFile + ) val shareIntent: Intent = Intent().apply { action = Intent.ACTION_SEND setDataAndType(apkURI, "image/jpeg") @@ -1731,9 +1795,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // but otherwise other apps will not be able to access our images unless we // scan them using [MediaScannerConnection] val mimeType = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(mediaFile.extension) + .getMimeTypeFromExtension(mediaFile.extension) MediaScannerConnection.scanFile( - this, arrayOf(mediaFile.absolutePath), arrayOf(mimeType), null) + this, arrayOf(mediaFile.absolutePath), arrayOf(mimeType), null + ) } private fun sendNewStoryReadyBroadcast(rawMediaFileList: List) { @@ -1755,7 +1820,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val arrayOfPaths = arrayOfNulls(mediaFileList.size) for ((index, mediaFile) in mediaFileList.withIndex()) { arrayOfmimeTypes[index] = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(mediaFile.extension) + .getMimeTypeFromExtension(mediaFile.extension) arrayOfPaths[index] = mediaFile.absolutePath } @@ -1763,77 +1828,82 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // but otherwise other apps will not be able to access our images unless we // scan them using [MediaScannerConnection] MediaScannerConnection.scanFile( - this, arrayOfPaths, arrayOfmimeTypes, null) + this, arrayOfPaths, arrayOfmimeTypes, null + ) } private fun blockTouchOnPhotoEditor(touchBlockMode: ScreenTouchBlockMode, message: String? = null) { - when (touchBlockMode) { - BLOCK_TOUCH_MODE_FULL_SCREEN -> { - translucent_view.visibility = View.VISIBLE - translucent_error_view.visibility = View.INVISIBLE - operation_text.text = message - translucent_view.setOnTouchListener { _, _ -> - // no op - true + contentComposerBinding.run { + when (touchBlockMode) { + BLOCK_TOUCH_MODE_FULL_SCREEN -> { + translucentView.visibility = View.VISIBLE + translucentErrorView.visibility = View.INVISIBLE + operationText.text = message + translucentView.setOnTouchListener { _, _ -> + // no op + true + } } - } - BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION -> { - translucent_view.visibility = View.GONE - translucent_error_view.visibility = View.VISIBLE - translucent_error_view.background = ColorDrawable( - ContextCompat.getColor(this, R.color.black_transp_error_scrim) - ) - translucent_error_view.setOnTouchListener { _, _ -> - // no op - true + BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION -> { + translucentView.visibility = View.GONE + translucentErrorView.visibility = View.VISIBLE + translucentErrorView.background = ColorDrawable( + ContextCompat.getColor(root.context, R.color.black_transp_error_scrim) + ) + translucentErrorView.setOnTouchListener { _, _ -> + // no op + true + } } - } - // do block touch but don't show scrim (make it transparent) - BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY -> { - translucent_view.visibility = View.GONE - translucent_error_view.visibility = View.VISIBLE - translucent_error_view.background = ColorDrawable( - ContextCompat.getColor(this, android.R.color.transparent) - ) - translucent_error_view.setOnTouchListener { _, _ -> - // no op - true + // do block touch but don't show scrim (make it transparent) + BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY -> { + translucentView.visibility = View.GONE + translucentErrorView.visibility = View.VISIBLE + translucentErrorView.background = ColorDrawable( + ContextCompat.getColor(root.context, android.R.color.transparent) + ) + translucentErrorView.setOnTouchListener { _, _ -> + // no op + true + } } - } - // block touch, no scrim, tapping releases block - BLOCK_TOUCH_MODE_DELETE_SLIDE -> { - translucent_view.visibility = View.GONE - translucent_error_view.visibility = View.VISIBLE - translucent_error_view.background = ColorDrawable( - ContextCompat.getColor(this, android.R.color.transparent) - ) - translucent_error_view.setOnTouchListener { _, _ -> - // If the error view is tapped, dismiss it and cancel delete slide mode - // Don't consume the touch event, pass it along - disableDeleteSlideMode() - false + // block touch, no scrim, tapping releases block + BLOCK_TOUCH_MODE_DELETE_SLIDE -> { + translucentView.visibility = View.GONE + translucentErrorView.visibility = View.VISIBLE + translucentErrorView.background = ColorDrawable( + ContextCompat.getColor(root.context, android.R.color.transparent) + ) + translucentErrorView.setOnTouchListener { _, _ -> + // If the error view is tapped, dismiss it and cancel delete slide mode + // Don't consume the touch event, pass it along + disableDeleteSlideMode() + false + } + } + // just don't block touch + BLOCK_TOUCH_MODE_NONE -> { + translucentView.visibility = View.GONE + translucentErrorView.visibility = View.GONE } - } - // just don't block touch - BLOCK_TOUCH_MODE_NONE -> { - translucent_view.visibility = View.GONE - translucent_error_view.visibility = View.GONE } } } private fun releaseTouchOnPhotoEditor(touchBlockMode: ScreenTouchBlockMode) { - when (touchBlockMode) { - BLOCK_TOUCH_MODE_FULL_SCREEN -> { - translucent_view.visibility = View.GONE - operation_text.text = null - translucent_view.setOnTouchListener(null) - } - BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION, - BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY, - BLOCK_TOUCH_MODE_NONE -> { - translucent_error_view.visibility = View.GONE - translucent_error_view.setOnTouchListener(null) + contentComposerBinding.run { + when (touchBlockMode) { + BLOCK_TOUCH_MODE_FULL_SCREEN -> { + translucentView.visibility = View.GONE + operationText.text = null + translucentView.setOnTouchListener(null) + } + BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION, + BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY, + BLOCK_TOUCH_MODE_NONE -> { + translucentErrorView.visibility = View.GONE + translucentErrorView.setOnTouchListener(null) + } } } } @@ -1898,9 +1968,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } else { val model = (source as? FileBackgroundSource)?.file ?: (source as UriBackgroundSource).contentUri Glide.with(this@ComposeLoopFrameActivity) - .load(model) - .transform(CenterCrop()) - .into(photoEditorView.source) + .load(model) + .transform(CenterCrop()) + .into(contentComposerBinding.photoEditorView.source) showStaticBackground() } @@ -1942,7 +2012,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun toggleDeleteSlideMode() { - if (delete_slide_view.visibility == View.VISIBLE) { + if (contentComposerBinding.deleteSlideView.visibility == View.VISIBLE) { disableDeleteSlideMode() } else { enableDeleteSlideMode() @@ -1950,13 +2020,13 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } private fun enableDeleteSlideMode() { - delete_slide_view.visibility = View.VISIBLE + contentComposerBinding.deleteSlideView.visibility = View.VISIBLE editModeHideAllUIControls(hideNextButton = true, hideFrameSelector = false) blockTouchOnPhotoEditor(BLOCK_TOUCH_MODE_DELETE_SLIDE) } private fun disableDeleteSlideMode() { - delete_slide_view.visibility = View.GONE + contentComposerBinding.deleteSlideView.visibility = View.GONE editModeRestoreAllUIControls() releaseTouchOnPhotoEditor(BLOCK_TOUCH_MODE_NONE) } @@ -1993,7 +2063,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // as a swipe from bottom of the screen gesture // in edit mode, show Emoji picker - if (edit_mode_controls.visibility == View.VISIBLE) { + if (contentComposerBinding.editModeControls.visibility == View.VISIBLE) { emojiPickerFragment.show(supportFragmentManager, emojiPickerFragment.tag) } else { // in capture mode, show media picker @@ -2002,7 +2072,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } return false } else if (e2.y - e1.y > SWIPE_MIN_DISTANCE && - abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { + abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { // Top to bottom return false } @@ -2083,6 +2153,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec class ExternalMediaPickerRequestCodesAndExtraKeys { var PHOTO_PICKER: Int = 0 // default code, can be overriden. + // Leave this value in zero so it's evident if something is not working (will break // if not properly initialized) lateinit var EXTRA_MEDIA_URIS: String diff --git a/stories/src/main/res/layout/activity_composer.xml b/stories/src/main/res/layout/activity_composer.xml index 34bf46cd1..94df9d29e 100644 --- a/stories/src/main/res/layout/activity_composer.xml +++ b/stories/src/main/res/layout/activity_composer.xml @@ -8,6 +8,6 @@ android:fitsSystemWindows="true" tools:context="com.wordpress.stories.compose.ComposeLoopFrameActivity"> - + From fa52e5976c6c737217ff77c6501fede5a6081488 Mon Sep 17 00:00:00 2001 From: faranjit Date: Sun, 24 Jan 2021 14:55:00 +0300 Subject: [PATCH 3/7] replaced kotlin synthetic with viewbinding --- app/src/main/java/com/automattic/loop/Loop.kt | 9 +++ .../automattic/loop/intro/IntroActivity.kt | 4 ++ .../automattic/loop/intro/IntroFragment.kt | 3 +- .../wordpress/stories/compose/SaveButton.kt | 57 ++++++++------- .../compose/emoji/EmojiPickerFragment.kt | 31 ++++---- .../story/StoryFrameSelectorAdapter.kt | 52 ++++++-------- .../story/StoryFrameSelectorFragment.kt | 57 ++++++++------- .../compose/text/TextColorPickerAdapter.kt | 30 ++++---- .../compose/text/TextEditorDialogFragment.kt | 72 ++++++++++--------- 9 files changed, 165 insertions(+), 150 deletions(-) diff --git a/app/src/main/java/com/automattic/loop/Loop.kt b/app/src/main/java/com/automattic/loop/Loop.kt index 351f3619c..c86c10a5b 100644 --- a/app/src/main/java/com/automattic/loop/Loop.kt +++ b/app/src/main/java/com/automattic/loop/Loop.kt @@ -14,6 +14,7 @@ import com.automattic.loop.R.string import com.automattic.loop.util.CrashLoggingUtils import com.wordpress.stories.compose.NotificationTrackerProvider import com.wordpress.stories.compose.frame.StoryNotificationType +import com.wordpress.stories.util.LOG_TAG open class Loop : Application(), NotificationTrackerProvider { private var statusBarHeight: Int = 0 @@ -22,6 +23,8 @@ open class Loop : Application(), NotificationTrackerProvider { super.onCreate() AppPrefs.init(this) + Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler()) + CrashLoggingUtils.startCrashLogging(this) createNotificationChannelsOnSdk26() @@ -136,4 +139,10 @@ open class Loop : Application(), NotificationTrackerProvider { // example Toast.makeText(this, "Notification dismissed! : " + storyNotificationType, Toast.LENGTH_LONG).show() } + + class ExceptionHandler: Thread.UncaughtExceptionHandler { + override fun uncaughtException(t: Thread?, e: Throwable?) { + Log.e(LOG_TAG, "", e) + } + } } diff --git a/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt b/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt index d0cec90af..556c02068 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt @@ -9,10 +9,14 @@ import androidx.navigation.Navigation import com.automattic.loop.AppPrefs import com.automattic.loop.MainActivity import com.automattic.loop.R +import com.automattic.loop.databinding.ActivityIntroBinding import com.automattic.photoeditor.util.PermissionUtils +import com.wordpress.stories.viewBinding class IntroActivity : AppCompatActivity(), IntroFragment.OnFragmentInteractionListener, PermissionRequestFragment.OnFragmentInteractionListener { + private val binding by viewBinding(ActivityIntroBinding::inflate) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt b/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt index 5a4669241..d044cfe27 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroFragment.kt @@ -7,7 +7,6 @@ import androidx.fragment.app.Fragment import com.automattic.loop.R import com.automattic.loop.databinding.FragmentIntroBinding import com.wordpress.stories.viewBinding -import kotlinx.android.synthetic.main.fragment_intro.* class IntroFragment : Fragment(R.layout.fragment_intro) { interface OnFragmentInteractionListener { @@ -29,7 +28,7 @@ class IntroFragment : Fragment(R.layout.fragment_intro) { introPager.adapter = IntroPagerAdapter(childFragmentManager) // Using a TabLayout for simulating a page indicator strip - tabLayoutIndicator.setupWithViewPager(intro_pager, true) + tabLayoutIndicator.setupWithViewPager(binding.introPager, true) } } diff --git a/stories/src/main/java/com/wordpress/stories/compose/SaveButton.kt b/stories/src/main/java/com/wordpress/stories/compose/SaveButton.kt index 64a8c81b7..507ea392a 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/SaveButton.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/SaveButton.kt @@ -2,12 +2,12 @@ package com.wordpress.stories.compose import android.content.Context import android.util.AttributeSet +import android.view.LayoutInflater import android.view.View import android.widget.FrameLayout import androidx.core.content.ContextCompat.getDrawable import com.wordpress.stories.R - -import kotlinx.android.synthetic.main.content_save_button.view.* +import com.wordpress.stories.databinding.ContentSaveButtonBinding class SaveButton @JvmOverloads constructor( context: Context, @@ -17,40 +17,47 @@ class SaveButton @JvmOverloads constructor( private var savingState = false private var saveButtonClickListener: OnClickListener? = null + private var binding: ContentSaveButtonBinding? = null + init { - val view = View.inflate(context, R.layout.content_save_button, null) - addView(view) + binding = ContentSaveButtonBinding.inflate(LayoutInflater.from(context)) +// val view = View.inflate(context, R.layout.content_save_button, null) + addView(binding?.root) setSaving(false) } fun setSaving(isSaving: Boolean) { savingState = isSaving - if (savingState) { - save_button_text.setText(R.string.label_control_saving) - save_button_icon.visibility = View.INVISIBLE - save_button_spinner.visibility = View.VISIBLE - save_button_saved_icon.visibility = View.INVISIBLE - background = getDrawable(context, R.drawable.save_button_background_disabled) - } else { - save_button_text.setText(R.string.label_control_retry) - save_button_icon.visibility = View.VISIBLE - save_button_spinner.visibility = View.INVISIBLE - save_button_saved_icon.visibility = View.INVISIBLE - background = getDrawable(context, R.drawable.save_button_background_enabled) + binding?.run { + if (savingState) { + saveButtonText.setText(R.string.label_control_saving) + saveButtonIcon.visibility = View.INVISIBLE + saveButtonSpinner.visibility = View.VISIBLE + saveButtonSavedIcon.visibility = View.INVISIBLE + background = getDrawable(context, R.drawable.save_button_background_disabled) + } else { + saveButtonText.setText(R.string.label_control_retry) + saveButtonIcon.visibility = View.VISIBLE + saveButtonSpinner.visibility = View.INVISIBLE + saveButtonSavedIcon.visibility = View.INVISIBLE + background = getDrawable(context, R.drawable.save_button_background_enabled) + } } } fun showSavedAnimation(callback: Runnable?) { if (savingState) { - savingState = false - save_button_text.setText(R.string.label_control_saved) - save_button_icon.visibility = View.INVISIBLE - save_button_spinner.visibility = View.INVISIBLE - save_button_saved_icon.visibility = View.VISIBLE - background = getDrawable(context, R.drawable.save_button_background_disabled) - postDelayed({ - callback?.run() - }, 250) + binding?.run { + savingState = false + saveButtonText.setText(R.string.label_control_saved) + saveButtonIcon.visibility = View.INVISIBLE + saveButtonSpinner.visibility = View.INVISIBLE + saveButtonSavedIcon.visibility = View.VISIBLE + background = getDrawable(context, R.drawable.save_button_background_disabled) + postDelayed({ + callback?.run() + }, 250) + } } } diff --git a/stories/src/main/java/com/wordpress/stories/compose/emoji/EmojiPickerFragment.kt b/stories/src/main/java/com/wordpress/stories/compose/emoji/EmojiPickerFragment.kt index e8c26b34b..e56a2ffbf 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/emoji/EmojiPickerFragment.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/emoji/EmojiPickerFragment.kt @@ -6,19 +6,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.emoji.text.EmojiCompat import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.automattic.photoeditor.PhotoEditor -import com.wordpress.stories.compose.hideStatusBar -import com.wordpress.stories.util.getDisplayPixelWidth import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.wordpress.stories.R -import kotlinx.android.synthetic.main.fragment_bottom_sticker_emoji_dialog.view.* -import kotlinx.android.synthetic.main.row_emoji.view.* +import com.wordpress.stories.compose.hideStatusBar +import com.wordpress.stories.databinding.FragmentBottomStickerEmojiDialogBinding +import com.wordpress.stories.databinding.RowEmojiBinding +import com.wordpress.stories.util.getDisplayPixelWidth class EmojiPickerFragment : BottomSheetDialogFragment() { private var listener: EmojiListener? = null @@ -30,6 +29,7 @@ class EmojiPickerFragment : BottomSheetDialogFragment() { dismiss() } } + override fun onSlide(bottomSheet: View, slideOffset: Float) { // Tweak to make swipe down fully dismiss the view because of this issue: // Swiping down on the sheet, the stickers get stuck half way @@ -77,15 +77,16 @@ class EmojiPickerFragment : BottomSheetDialogFragment() { override fun setupDialog(dialog: Dialog, style: Int) { super.setupDialog(dialog, style) activity?.let { activity -> - val contentView = View.inflate(context, R.layout.fragment_bottom_sticker_emoji_dialog, null) - dialog.setContentView(contentView) - val params = (contentView.parent as View).layoutParams as CoordinatorLayout.LayoutParams + val binding = FragmentBottomStickerEmojiDialogBinding.inflate(LayoutInflater.from(context)) +// val contentView = View.inflate(context, R.layout.fragment_bottom_sticker_emoji_dialog, null) + dialog.setContentView(binding.root) + val params = binding.root.layoutParams as CoordinatorLayout.LayoutParams (params.behavior as? BottomSheetBehavior)?.setBottomSheetCallback(bottomSheetBehaviorCallback) (params.behavior as? BottomSheetBehavior)?.state = BottomSheetBehavior.STATE_EXPANDED - contentView.rvEmoji.layoutManager = GridLayoutManager(activity, COLUMNS) - contentView.rvEmoji.adapter = EmojiAdapter(PhotoEditor.getEmojis(activity)) + binding.rvEmoji.layoutManager = GridLayoutManager(activity, COLUMNS) + binding.rvEmoji.adapter = EmojiAdapter(PhotoEditor.getEmojis(activity)) } } @@ -103,7 +104,7 @@ class EmojiPickerFragment : BottomSheetDialogFragment() { params.width = it view.layoutParams = params } - return ViewHolder(view) + return ViewHolder(RowEmojiBinding.inflate(LayoutInflater.from(parent.context), parent, false)) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { @@ -113,14 +114,14 @@ class EmojiPickerFragment : BottomSheetDialogFragment() { EmojiCompat.get().unregisterInitCallback(this) val compat = EmojiCompat.get() - holder.txtEmojiRef.text = compat.process(emojiList[position]) + holder.binding.txtEmoji.text = compat.process(emojiList[position]) } override fun onFailed(throwable: Throwable?) { EmojiCompat.get().unregisterInitCallback(this) // just fallback to setting the text - holder.txtEmojiRef.text = emojiList[position] + holder.binding.txtEmoji.text = emojiList[position] } }) } @@ -129,9 +130,7 @@ class EmojiPickerFragment : BottomSheetDialogFragment() { return emojiList.size } - inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - var txtEmojiRef: TextView = itemView.txtEmoji - + inner class ViewHolder(val binding: RowEmojiBinding) : RecyclerView.ViewHolder(binding.root) { init { itemView.setOnClickListener { listener?.onEmojiClick(emojiList[layoutPosition]) diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt index 0076a81a2..34e976602 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt @@ -4,17 +4,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.webkit.URLUtil -import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView -import com.wordpress.stories.compose.story.StoryFrameSelectorAdapter.StoryFrameHolder.StoryFrameHolderItem -import com.wordpress.stories.compose.story.StoryViewModel.StoryFrameListItemUiState -import com.wordpress.stories.compose.story.StoryViewModel.StoryFrameListItemUiState.StoryFrameListItemUiStateFrame import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestOptions -import com.wordpress.stories.R -import kotlinx.android.synthetic.main.fragment_story_frame_item.view.* +import com.wordpress.stories.compose.story.StoryFrameSelectorAdapter.StoryFrameHolder.StoryFrameHolderItem +import com.wordpress.stories.compose.story.StoryViewModel.StoryFrameListItemUiState +import com.wordpress.stories.compose.story.StoryViewModel.StoryFrameListItemUiState.StoryFrameListItemUiStateFrame +import com.wordpress.stories.databinding.FragmentStoryFrameItemBinding class StoryFrameSelectorAdapter : RecyclerView.Adapter() { private val items = mutableListOf() @@ -26,9 +24,10 @@ class StoryFrameSelectorAdapter : RecyclerView.Adapter StoryFrameHolderItem( - LayoutInflater - .from(parent.context) - .inflate(R.layout.fragment_story_frame_item, parent, false) + FragmentStoryFrameItemBinding.inflate( + LayoutInflater + .from(parent.context) + ) ) else -> throw NotImplementedError("Unknown ViewType") } @@ -71,14 +70,11 @@ class StoryFrameSelectorAdapter : RecyclerView.Adapter Unit)? = null private var onFrameLongPressed: (() -> Unit)? = null @@ -100,15 +96,15 @@ class StoryFrameSelectorAdapter : RecyclerView.Adapter Unit @@ -23,26 +23,25 @@ class TextColorPickerAdapter internal constructor(private val context: Context, private var selectedPosition = colorPickerColors.indexOf(startColor) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(context).inflate(R.layout.color_picker_list_item, parent, false) - return ViewHolder(view) + return ViewHolder(ColorPickerListItemBinding.inflate(LayoutInflater.from(context))) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.itemView.isSelected = (selectedPosition == position) if (colorPickerColors[position] == Color.TRANSPARENT) { - holder.colorPickerActualColorViewRef.setBackgroundResource(R.drawable.ic_gridicons_block_cropped) - holder.colorPickerSelectionViewRef.visibility = View.GONE + holder.binding.colorPickerViewActualColor.setBackgroundResource(R.drawable.ic_gridicons_block_cropped) + holder.binding.colorPickerSelectedCheckmarkView.visibility = View.GONE } else { - holder.colorPickerActualColorViewRef.setBackgroundResource(R.drawable.ic_color_picker_filler) - val background = holder.colorPickerActualColorViewRef.background + holder.binding.colorPickerViewActualColor.setBackgroundResource(R.drawable.ic_color_picker_filler) + val background = holder.binding.colorPickerSelectedCheckmarkView.background if (background is GradientDrawable) { background.setColor(colorPickerColors[position]) } else if (background is ColorDrawable) { background.color = colorPickerColors[position] } - holder.colorPickerSelectionViewRef.visibility = if (holder.itemView.isSelected) View.VISIBLE else View.GONE + holder.binding.colorPickerSelectedCheckmarkView.visibility = if (holder.itemView.isSelected) View.VISIBLE else View.GONE } } @@ -52,10 +51,7 @@ class TextColorPickerAdapter internal constructor(private val context: Context, this.onTextColorPickerClickListener = onTextColorPickerClickListener } - inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - var colorPickerActualColorViewRef: View = itemView.color_picker_view_actual_color - var colorPickerSelectionViewRef: View = itemView.color_picker_selected_checkmark_view - + inner class ViewHolder(val binding: ColorPickerListItemBinding) : RecyclerView.ViewHolder(binding.root) { init { itemView.setOnClickListener { notifyItemChanged(selectedPosition) @@ -73,10 +69,12 @@ class TextColorPickerAdapter internal constructor(private val context: Context, enum class Mode { FOREGROUND, BACKGROUND } fun getDefaultColors(context: Context, mode: Mode): List { - val baseArray = context.resources.obtainTypedArray(when (mode) { - Mode.FOREGROUND -> R.array.text_colors - Mode.BACKGROUND -> R.array.text_background_colors - }) + val baseArray = context.resources.obtainTypedArray( + when (mode) { + Mode.FOREGROUND -> R.array.text_colors + Mode.BACKGROUND -> R.array.text_background_colors + } + ) return baseArray.extractColorList(context.resources).also { baseArray.recycle() } } diff --git a/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt b/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt index a0ef70576..3b84b961f 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt @@ -22,14 +22,15 @@ import com.automattic.photoeditor.text.TextStyler import com.wordpress.stories.R import com.wordpress.stories.compose.StoriesAnalyticsListener import com.wordpress.stories.compose.text.TextColorPickerAdapter.Companion.Mode -import kotlinx.android.synthetic.main.add_text_dialog.* -import kotlinx.android.synthetic.main.color_picker_bottom_sheet.* +import com.wordpress.stories.databinding.AddTextDialogBinding /** * Created by Burhanuddin Rashid on 1/16/2018. */ class TextEditorDialogFragment : DialogFragment() { + private lateinit var binding: AddTextDialogBinding + @ColorInt private var colorCode: Int = 0 @ColorInt private var backgroundColorCode: Int = Color.TRANSPARENT @@ -69,7 +70,8 @@ class TextEditorDialogFragment : DialogFragment() { } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.add_text_dialog, container, false) + binding = AddTextDialogBinding.inflate(inflater) + return binding.root } override fun onStop() { @@ -82,7 +84,7 @@ class TextEditorDialogFragment : DialogFragment() { super.onViewCreated(view, savedInstanceState) arguments?.let { - add_text_edit_text.setText(it.getString(EXTRA_INPUT_TEXT)) + binding.addTextEditText.setText(it.getString(EXTRA_INPUT_TEXT)) colorCode = it.getInt(EXTRA_TEXT_COLOR_CODE) backgroundColorCode = it.getInt(EXTRA_TEXT_BACKGROUND_COLOR_CODE) textAlignment = TextAlignment.valueOf(it.getInt(EXTRA_TEXT_ALIGNMENT)) @@ -94,35 +96,35 @@ class TextEditorDialogFragment : DialogFragment() { } // Hide the bottom sheet if the user taps in the EditText - add_text_edit_text.setOnClickListener { + binding.addTextEditText.setOnClickListener { bottomSheetHandler?.hideBottomSheet() } - textSizeSlider = TextSizeSlider(text_size_slider, add_text_edit_text, resources) { + textSizeSlider = TextSizeSlider(binding.textSizeSlider, binding.addTextEditText, resources) { textStyleGroupManager.customFontSizeApplied = true } initTextColoring() - text_alignment_button.setOnClickListener { + binding.textAlignmentButton.setOnClickListener { textAlignment = TextAlignment.getNext(textAlignment) updateTextAlignment(textAlignment) } - text_style_toggle_button?.setOnClickListener { + binding.textStyleToggleButton.setOnClickListener { typefaceId = textStyleGroupManager.getNextTypeface(typefaceId) - textStyleGroupManager.styleTextView(typefaceId, add_text_edit_text) - textStyleGroupManager.styleAndLabelTextView(typefaceId, text_style_toggle_button) + textStyleGroupManager.styleTextView(typefaceId, binding.addTextEditText) + textStyleGroupManager.styleAndLabelTextView(typefaceId, binding.textStyleToggleButton) trackTextStyleToggled() textSizeSlider.update() } - color_picker_button.setOnClickListener { + binding.colorPickerButton.setOnClickListener { bottomSheetHandler?.toggleBottomSheet() } // Apply any existing styling to text - add_text_edit_text.setTextColor(colorCode) + binding.addTextEditText.setTextColor(colorCode) applyBackgroundColor(backgroundColorCode) updateTextAlignment(textAlignment) @@ -130,25 +132,25 @@ class TextEditorDialogFragment : DialogFragment() { // This first time pass the font size so we know if we should fix the size val initialTextSize = arguments?.getFloat(EXTRA_TEXT_SIZE) ?: 0F - textStyleGroupManager.styleTextView(typefaceId, add_text_edit_text, initialTextSize) - textStyleGroupManager.styleAndLabelTextView(typefaceId, text_style_toggle_button) + textStyleGroupManager.styleTextView(typefaceId, binding.addTextEditText, initialTextSize) + textStyleGroupManager.styleAndLabelTextView(typefaceId, binding.textStyleToggleButton) textSizeSlider.update() updateColorPickerButton() - add_text_edit_text.requestFocus() + binding.addTextEditText.requestFocus() // Make a callback on activity when user is done with text editing - add_text_done_button?.setOnClickListener { + binding.addTextDoneButton.setOnClickListener { dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) dismiss() } } override fun onDismiss(dialog: DialogInterface) { - val inputText = add_text_edit_text?.text.toString() - textEditor?.onDone(inputText, TextStyler.from(add_text_edit_text, typefaceId, backgroundColorCode)) + val inputText = binding.addTextEditText?.text.toString() + textEditor?.onDone(inputText, TextStyler.from(binding.addTextEditText, typefaceId, backgroundColorCode)) textEditorAnalyticsHandler?.report() super.onDismiss(dialog) } @@ -169,11 +171,11 @@ class TextEditorDialogFragment : DialogFragment() { val textColorPickerAdapter = TextColorPickerAdapter(it, Mode.FOREGROUND, colorCode).apply { setOnColorPickerClickListener { colorCode -> this@TextEditorDialogFragment.colorCode = colorCode - add_text_edit_text?.setTextColor(colorCode) + binding.addTextEditText.setTextColor(colorCode) updateColorPickerButton() } } - with(text_color_picker_recycler_view) { + with(binding.colorPickerBottomSheet.textColorPickerRecyclerView) { this.layoutManager = LinearLayoutManager(it, LinearLayoutManager.HORIZONTAL, false) setHasFixedSize(true) this.adapter = textColorPickerAdapter @@ -186,10 +188,10 @@ class TextEditorDialogFragment : DialogFragment() { applyBackgroundColor(colorCode) updateColorPickerButton() // Reapply the styles, since text shadow depends on the background color + style combination - textStyleGroupManager.styleTextView(typefaceId, add_text_edit_text) + textStyleGroupManager.styleTextView(typefaceId, binding.addTextEditText) } } - with(text_background_color_picker_recycler_view) { + with(binding.colorPickerBottomSheet.textBackgroundColorPickerRecyclerView) { this.layoutManager = LinearLayoutManager(it, LinearLayoutManager.HORIZONTAL, false) setHasFixedSize(true) this.adapter = textBgColorPickerAdapter @@ -205,18 +207,20 @@ class TextEditorDialogFragment : DialogFragment() { // intended, so this gravity/text alignment dichotomy seems necessary.) // We should still set the textAlignment value though to make it easier to extract style values from the // EditText when the fragment is dismissed. - add_text_edit_text.textAlignment = textAlignment.value - add_text_edit_text.gravity = when (textAlignment) { + binding.addTextEditText.textAlignment = textAlignment.value + binding.addTextEditText.gravity = when (textAlignment) { TextAlignment.LEFT -> Gravity.START or Gravity.CENTER_VERTICAL TextAlignment.CENTER -> Gravity.CENTER_HORIZONTAL or Gravity.CENTER_VERTICAL TextAlignment.RIGHT -> Gravity.END or Gravity.CENTER_VERTICAL } - text_alignment_button.setImageResource(when (textAlignment) { - TextAlignment.LEFT -> R.drawable.ic_gridicons_align_left_32 - TextAlignment.CENTER -> R.drawable.ic_gridicons_align_center_32 - TextAlignment.RIGHT -> R.drawable.ic_gridicons_align_right_32 - }) + binding.textAlignmentButton.setImageResource( + when (textAlignment) { + TextAlignment.LEFT -> R.drawable.ic_gridicons_align_left_32 + TextAlignment.CENTER -> R.drawable.ic_gridicons_align_center_32 + TextAlignment.RIGHT -> R.drawable.ic_gridicons_align_right_32 + } + ) // The background span needs to be re-applied to adjust for the alignment change applyBackgroundColor(backgroundColorCode) @@ -227,7 +231,7 @@ class TextEditorDialogFragment : DialogFragment() { * Will clear any background color spans if [colorCode] is [Color.TRANSPARENT]. */ private fun applyBackgroundColor(@ColorInt colorCode: Int) { - add_text_edit_text.text?.apply { + binding.addTextEditText.text?.apply { // Clear any existing background color spans getSpans(0, length, RoundedBackgroundColorSpan::class.java)?.forEach { removeSpan(it) } if (colorCode != Color.TRANSPARENT) { @@ -242,7 +246,7 @@ class TextEditorDialogFragment : DialogFragment() { */ private fun updateColorPickerButton() { context?.let { context -> - PathAwareVectorDrawable(context, R.drawable.ic_textcolor, color_picker_button).apply { + PathAwareVectorDrawable(context, R.drawable.ic_textcolor, binding.colorPickerButton).apply { // The path names used need to be defined as 'name' properties in the drawable. setPathFillColor("inner-fill", colorCode) setPathFillColor("outer-fill", backgroundColorCode) @@ -273,8 +277,10 @@ class TextEditorDialogFragment : DialogFragment() { arguments = Bundle().apply { putString(EXTRA_INPUT_TEXT, inputText) - putInt(EXTRA_TEXT_COLOR_CODE, textStyler?.textColor - ?: ContextCompat.getColor(appCompatActivity, android.R.color.white)) + putInt( + EXTRA_TEXT_COLOR_CODE, textStyler?.textColor + ?: ContextCompat.getColor(appCompatActivity, android.R.color.white) + ) putInt(EXTRA_TEXT_BACKGROUND_COLOR_CODE, textStyler?.textBackgroundColor ?: Color.TRANSPARENT) putInt(EXTRA_TEXT_ALIGNMENT, textStyler?.textAlignment ?: TextAlignment.default()) putInt(EXTRA_TYPEFACE, textStyler?.typefaceId ?: TextStyleGroupManager.TYPEFACE_ID_NUNITO) From 2576fcc2ebc774525f8fca1111a934c26fdb8ff7 Mon Sep 17 00:00:00 2001 From: faranjit Date: Sun, 24 Jan 2021 15:04:35 +0300 Subject: [PATCH 4/7] revert exception handler --- app/src/main/java/com/automattic/loop/Loop.kt | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/automattic/loop/Loop.kt b/app/src/main/java/com/automattic/loop/Loop.kt index c86c10a5b..8f6812a7a 100644 --- a/app/src/main/java/com/automattic/loop/Loop.kt +++ b/app/src/main/java/com/automattic/loop/Loop.kt @@ -14,7 +14,6 @@ import com.automattic.loop.R.string import com.automattic.loop.util.CrashLoggingUtils import com.wordpress.stories.compose.NotificationTrackerProvider import com.wordpress.stories.compose.frame.StoryNotificationType -import com.wordpress.stories.util.LOG_TAG open class Loop : Application(), NotificationTrackerProvider { private var statusBarHeight: Int = 0 @@ -23,8 +22,6 @@ open class Loop : Application(), NotificationTrackerProvider { super.onCreate() AppPrefs.init(this) - Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler()) - CrashLoggingUtils.startCrashLogging(this) createNotificationChannelsOnSdk26() @@ -38,22 +35,22 @@ open class Loop : Application(), NotificationTrackerProvider { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Create the NORMAL channel (used for likes, comments, replies, etc.) val normalChannel = NotificationChannel( - getString(string.notification_channel_normal_id), - getString(string.notification_channel_general_title), - NotificationManager.IMPORTANCE_DEFAULT + getString(string.notification_channel_normal_id), + getString(string.notification_channel_general_title), + NotificationManager.IMPORTANCE_DEFAULT ) // Register the channel with the system; you can't change the importance // or other notification behaviors after this val notificationManager = getSystemService( - NOTIFICATION_SERVICE + NOTIFICATION_SERVICE ) as NotificationManager notificationManager.createNotificationChannel(normalChannel) // Create the IMPORTANT channel (used for 2fa auth, for example) val importantChannel = NotificationChannel( - getString(string.notification_channel_important_id), - getString(string.notification_channel_important_title), - NotificationManager.IMPORTANCE_HIGH + getString(string.notification_channel_important_id), + getString(string.notification_channel_important_title), + NotificationManager.IMPORTANCE_HIGH ) // Register the channel with the system; you can't change the importance // or other notification behaviors after this @@ -61,9 +58,9 @@ open class Loop : Application(), NotificationTrackerProvider { // Create the REMINDER channel (used for various reminders, like Quick Start, etc.) val reminderChannel = NotificationChannel( - getString(string.notification_channel_reminder_id), - getString(string.notification_channel_reminder_title), - NotificationManager.IMPORTANCE_LOW + getString(string.notification_channel_reminder_id), + getString(string.notification_channel_reminder_title), + NotificationManager.IMPORTANCE_LOW ) // Register the channel with the system; you can't change the importance // or other notification behaviors after this @@ -72,9 +69,9 @@ open class Loop : Application(), NotificationTrackerProvider { // Create the TRANSIENT channel (used for short-lived notifications such as processing a Like/Approve, // or media upload) val transientChannel = NotificationChannel( - getString(string.notification_channel_transient_id), - getString(string.notification_channel_transient_title), - NotificationManager.IMPORTANCE_DEFAULT + getString(string.notification_channel_transient_id), + getString(string.notification_channel_transient_title), + NotificationManager.IMPORTANCE_DEFAULT ).apply { setSound(null, null) enableVibration(false) @@ -91,10 +88,10 @@ open class Loop : Application(), NotificationTrackerProvider { // Use a downloadable font for EmojiCompat val fontRequest = FontRequest( - "com.google.android.gms.fonts", - "com.google.android.gms", - "Noto Color Emoji Compat", - array.com_google_android_gms_fonts_certs + "com.google.android.gms.fonts", + "com.google.android.gms", + "Noto Color Emoji Compat", + array.com_google_android_gms_fonts_certs ) config = FontRequestEmojiCompatConfig(applicationContext, fontRequest) config.setReplaceAll(true) @@ -139,10 +136,4 @@ open class Loop : Application(), NotificationTrackerProvider { // example Toast.makeText(this, "Notification dismissed! : " + storyNotificationType, Toast.LENGTH_LONG).show() } - - class ExceptionHandler: Thread.UncaughtExceptionHandler { - override fun uncaughtException(t: Thread?, e: Throwable?) { - Log.e(LOG_TAG, "", e) - } - } } From 154ecc40e4cc604aecb90e1ff957665664210c86 Mon Sep 17 00:00:00 2001 From: faranjit Date: Sun, 24 Jan 2021 15:21:17 +0300 Subject: [PATCH 5/7] lint errors --- app/src/main/java/com/automattic/loop/Loop.kt | 34 +++++++++---------- .../loop/intro/IntroPagerAdapter.kt | 4 ++- .../stories/ByActivityViewBinding.kt | 7 ++-- .../compose/ComposeLoopFrameActivity.kt | 30 +++++++++------- .../compose/text/TextColorPickerAdapter.kt | 6 +++- 5 files changed, 47 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/automattic/loop/Loop.kt b/app/src/main/java/com/automattic/loop/Loop.kt index 8f6812a7a..351f3619c 100644 --- a/app/src/main/java/com/automattic/loop/Loop.kt +++ b/app/src/main/java/com/automattic/loop/Loop.kt @@ -35,22 +35,22 @@ open class Loop : Application(), NotificationTrackerProvider { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Create the NORMAL channel (used for likes, comments, replies, etc.) val normalChannel = NotificationChannel( - getString(string.notification_channel_normal_id), - getString(string.notification_channel_general_title), - NotificationManager.IMPORTANCE_DEFAULT + getString(string.notification_channel_normal_id), + getString(string.notification_channel_general_title), + NotificationManager.IMPORTANCE_DEFAULT ) // Register the channel with the system; you can't change the importance // or other notification behaviors after this val notificationManager = getSystemService( - NOTIFICATION_SERVICE + NOTIFICATION_SERVICE ) as NotificationManager notificationManager.createNotificationChannel(normalChannel) // Create the IMPORTANT channel (used for 2fa auth, for example) val importantChannel = NotificationChannel( - getString(string.notification_channel_important_id), - getString(string.notification_channel_important_title), - NotificationManager.IMPORTANCE_HIGH + getString(string.notification_channel_important_id), + getString(string.notification_channel_important_title), + NotificationManager.IMPORTANCE_HIGH ) // Register the channel with the system; you can't change the importance // or other notification behaviors after this @@ -58,9 +58,9 @@ open class Loop : Application(), NotificationTrackerProvider { // Create the REMINDER channel (used for various reminders, like Quick Start, etc.) val reminderChannel = NotificationChannel( - getString(string.notification_channel_reminder_id), - getString(string.notification_channel_reminder_title), - NotificationManager.IMPORTANCE_LOW + getString(string.notification_channel_reminder_id), + getString(string.notification_channel_reminder_title), + NotificationManager.IMPORTANCE_LOW ) // Register the channel with the system; you can't change the importance // or other notification behaviors after this @@ -69,9 +69,9 @@ open class Loop : Application(), NotificationTrackerProvider { // Create the TRANSIENT channel (used for short-lived notifications such as processing a Like/Approve, // or media upload) val transientChannel = NotificationChannel( - getString(string.notification_channel_transient_id), - getString(string.notification_channel_transient_title), - NotificationManager.IMPORTANCE_DEFAULT + getString(string.notification_channel_transient_id), + getString(string.notification_channel_transient_title), + NotificationManager.IMPORTANCE_DEFAULT ).apply { setSound(null, null) enableVibration(false) @@ -88,10 +88,10 @@ open class Loop : Application(), NotificationTrackerProvider { // Use a downloadable font for EmojiCompat val fontRequest = FontRequest( - "com.google.android.gms.fonts", - "com.google.android.gms", - "Noto Color Emoji Compat", - array.com_google_android_gms_fonts_certs + "com.google.android.gms.fonts", + "com.google.android.gms", + "Noto Color Emoji Compat", + array.com_google_android_gms_fonts_certs ) config = FontRequestEmojiCompatConfig(applicationContext, fontRequest) config.setReplaceAll(true) diff --git a/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt b/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt index 522669a95..d1438db01 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroPagerAdapter.kt @@ -5,7 +5,9 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentPagerAdapter import com.automattic.loop.R -class IntroPagerAdapter(supportFragmentManager: FragmentManager) : FragmentPagerAdapter(supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { +class IntroPagerAdapter( + supportFragmentManager: FragmentManager +) : FragmentPagerAdapter(supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { override fun getItem(position: Int): Fragment { return if (position == 0) { IntroPagerTitleFragment.newInstance(R.string.intro_title_text, PROMO_TEXTS[position], diff --git a/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt b/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt index c9aa2cfc4..da9267ff0 100644 --- a/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt +++ b/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt @@ -19,7 +19,8 @@ fun AppCompatActivity.viewBinding(initializer: (LayoutInflater ViewBindingPropertyDelegate(this, initializer) class ViewBindingPropertyDelegate( - private val activity: AppCompatActivity, private val initializer: (LayoutInflater) -> T + private val activity: AppCompatActivity, + private val initializer: (LayoutInflater) -> T ) : ReadOnlyProperty, LifecycleObserver { private var binding: T? = null @@ -42,7 +43,9 @@ class ViewBindingPropertyDelegate( if (binding == null) { // This must be on the main thread only if (Looper.myLooper() != Looper.getMainLooper()) { - throw IllegalThreadStateException("This cannot be called from other threads. It should be on the main thread only.") + throw IllegalThreadStateException( + "This cannot be called from other threads. It should be on the main thread only." + ) } binding = initializer(thisRef.layoutInflater) 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 9b85f7428..c445a16f7 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -350,9 +350,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec photoEditor.updateWorkAreaRect(workingAreaRect) contentComposerBinding.deleteView.addBottomOffset(bottomNavigationBarMargin) contentComposerBinding.deleteSlideView.addBottomOffset(bottomNavigationBarMargin) - (supportFragmentManager.findFragmentById(R.id.bottom_strip_view) as? StoryFrameSelectorFragment)?.setBottomOffset( - bottomNavigationBarMargin - ) + (supportFragmentManager.findFragmentById(R.id.bottom_strip_view) + as? StoryFrameSelectorFragment)?.setBottomOffset(bottomNavigationBarMargin) insets } @@ -905,23 +904,27 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec override fun onHoldingGestureStart() { timesUpHandler.removeCallbacksAndMessages(null) - if (PermissionUtils.allVideoPermissionsGranted(this@ComposeLoopFrameActivity)) { + if (PermissionUtils.allVideoPermissionsGranted( + this@ComposeLoopFrameActivity) + ) { // if we at least have startRecordingVideoAfterVibrationIndication() } else { // request permissions including audio for video - val permissionName = PermissionUtils.anyVideoNeededPermissionPermanentlyDenied( - this@ComposeLoopFrameActivity - ) + val permissionName = + PermissionUtils.anyVideoNeededPermissionPermanentlyDenied( + this@ComposeLoopFrameActivity + ) permissionName?.let { showPermissionPermanentlyDeniedDialog(it) } - ?: PermissionUtils.requestAllRequiredPermissionsIncludingAudioForVideo( - this@ComposeLoopFrameActivity - ).also { - permissionsRequestForCameraInProgress = true - } + ?: PermissionUtils + .requestAllRequiredPermissionsIncludingAudioForVideo( + this@ComposeLoopFrameActivity + ).also { + permissionsRequestForCameraInProgress = true + } } } @@ -1689,7 +1692,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val originallyErrored = anyOfOriginalIntentResultsIsError() val currentlyErrored = storyViewModel.anyOfCurrentStoryFramesIsErrored() - val storyFrameSelectorFragment = supportFragmentManager.findFragmentById(R.id.bottom_strip_view) as? StoryFrameSelectorFragment + val storyFrameSelectorFragment = supportFragmentManager.findFragmentById(R.id.bottom_strip_view) + as? StoryFrameSelectorFragment contentComposerBinding.run { when { // if we were in an error-handling situation but now all pages are OK we're ready to go diff --git a/stories/src/main/java/com/wordpress/stories/compose/text/TextColorPickerAdapter.kt b/stories/src/main/java/com/wordpress/stories/compose/text/TextColorPickerAdapter.kt index d39a84ed3..07118e819 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/text/TextColorPickerAdapter.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/text/TextColorPickerAdapter.kt @@ -41,7 +41,11 @@ class TextColorPickerAdapter internal constructor(private val context: Context, background.color = colorPickerColors[position] } - holder.binding.colorPickerSelectedCheckmarkView.visibility = if (holder.itemView.isSelected) View.VISIBLE else View.GONE + holder.binding.colorPickerSelectedCheckmarkView.visibility = if (holder.itemView.isSelected) { + View.VISIBLE + } else { + View.GONE + } } } From bd69b0f8e3e42d732b72b60480c4a346f195fa0d Mon Sep 17 00:00:00 2001 From: faranjit Date: Sun, 24 Jan 2021 15:56:39 +0300 Subject: [PATCH 6/7] fix indents --- .../java/com/automattic/loop/MainActivity.kt | 30 +++++++++---------- .../java/com/automattic/loop/MainFragment.kt | 10 +++---- .../automattic/loop/intro/IntroActivity.kt | 4 +-- .../loop/intro/IntroPagerFragment.kt | 4 +-- .../loop/intro/IntroPagerTitleFragment.kt | 4 +-- .../loop/photopicker/PhotoPickerActivity.java | 24 +++++++-------- .../loop/photopicker/PhotoPickerAdapter.java | 16 +++++----- .../loop/photopicker/PhotoPickerFragment.java | 6 ++-- .../stories/ByActivityViewBinding.kt | 4 +-- .../stories/ByFragmentViewBinding.kt | 2 +- .../story/StoryFrameSelectorAdapter.kt | 16 +++++----- .../compose/text/TextColorPickerAdapter.kt | 10 +++---- .../compose/text/TextEditorDialogFragment.kt | 14 ++++----- 13 files changed, 72 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/com/automattic/loop/MainActivity.kt b/app/src/main/java/com/automattic/loop/MainActivity.kt index 893503107..4093491d6 100644 --- a/app/src/main/java/com/automattic/loop/MainActivity.kt +++ b/app/src/main/java/com/automattic/loop/MainActivity.kt @@ -60,7 +60,7 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList val bundle = Bundle() bundle.putBoolean(PhotoPickerActivity.EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED, true) Navigation.findNavController(this@MainActivity, R.id.nav_host_fragment) - .navigate(R.id.action_mainFragment_to_composeLoopFrameActivity, bundle) + .navigate(R.id.action_mainFragment_to_composeLoopFrameActivity, bundle) } } } @@ -71,7 +71,7 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList } override fun onSupportNavigateUp() = - Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() + Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() override fun onDestroy() { EventBus.getDefault().unregister(this) @@ -87,28 +87,28 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList val payloadString = it.getString(KEY_EXAMPLE_METADATA) val storyIndex = it.getInt(KEY_STORY_INDEX) Toast.makeText( - this, "Payload is: $payloadString - index: $storyIndex", - Toast.LENGTH_SHORT + this, "Payload is: $payloadString - index: $storyIndex", + Toast.LENGTH_SHORT ) - .show() + .show() } if (event.isSuccess()) { val text = String.format( - getString(R.string.story_saving_snackbar_finished_successfully), - StoryRepository.getStoryAtIndex(event.storyIndex).title + getString(R.string.story_saving_snackbar_finished_successfully), + StoryRepository.getStoryAtIndex(event.storyIndex).title ) Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show() } else { // show snackbar and add the PendingIntent with the StorySaveResult as a Serialized object if errors val errorText = String.format( - getString(R.string.story_saving_snackbar_finished_with_error), - StoryRepository.getStoryAtIndex(event.storyIndex).title + getString(R.string.story_saving_snackbar_finished_with_error), + StoryRepository.getStoryAtIndex(event.storyIndex).title ) val snackbarMessage = FrameSaveNotifier.buildSnackbarErrorMessage( - this, - event.frameSaveResult.count { it.resultReason is SaveError }, - errorText + this, + event.frameSaveResult.count { it.resultReason is SaveError }, + errorText ) val snackbar = Snackbar.make(findViewById(android.R.id.content), snackbarMessage, Snackbar.LENGTH_LONG) snackbar.setAction(R.string.story_saving_failed_quick_action_manage) { view -> @@ -122,7 +122,7 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList // from tapping on MANAGE on the snackbar (otherwise they'll be able to discard the // errored story but the error notification will remain existing in the system dashboard) intent.action = getNotificationIdForError( - StoryComposerActivity.BASE_FRAME_MEDIA_ERROR_NOTIFICATION_ID, event.storyIndex + StoryComposerActivity.BASE_FRAME_MEDIA_ERROR_NOTIFICATION_ID, event.storyIndex ).toString() + "" startActivity(intent) @@ -135,8 +135,8 @@ class MainActivity : AppCompatActivity(), MainFragment.OnFragmentInteractionList fun onStorySaveStart(event: StorySaveProcessStart) { EventBus.getDefault().removeStickyEvent(event) val text = String.format( - getString(R.string.story_saving_snackbar_started), - StoryRepository.getStoryAtIndex(event.storyIndex).title + getString(R.string.story_saving_snackbar_started), + StoryRepository.getStoryAtIndex(event.storyIndex).title ) Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show() } diff --git a/app/src/main/java/com/automattic/loop/MainFragment.kt b/app/src/main/java/com/automattic/loop/MainFragment.kt index f432c67dc..bceae6873 100644 --- a/app/src/main/java/com/automattic/loop/MainFragment.kt +++ b/app/src/main/java/com/automattic/loop/MainFragment.kt @@ -81,11 +81,11 @@ class MainFragment : Fragment(R.layout.fragment_main) { // TODO: Rename and change types and number of parameters @JvmStatic fun newInstance(param1: String, param2: String) = - MainFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } + MainFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) } + } } } diff --git a/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt b/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt index 556c02068..d5673874c 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroActivity.kt @@ -14,7 +14,7 @@ import com.automattic.photoeditor.util.PermissionUtils import com.wordpress.stories.viewBinding class IntroActivity : AppCompatActivity(), IntroFragment.OnFragmentInteractionListener, - PermissionRequestFragment.OnFragmentInteractionListener { + PermissionRequestFragment.OnFragmentInteractionListener { private val binding by viewBinding(ActivityIntroBinding::inflate) override fun onCreate(savedInstanceState: Bundle?) { @@ -25,7 +25,7 @@ class IntroActivity : AppCompatActivity(), IntroFragment.OnFragmentInteractionLi } override fun onSupportNavigateUp() = - Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() + Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp() override fun onGetStartedPressed() { AppPrefs.setIntroRequired(false) diff --git a/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt b/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt index 2539eeada..d25b39ddb 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroPagerFragment.kt @@ -29,8 +29,8 @@ class IntroPagerFragment : Fragment(R.layout.intro_template_view) { with(view) { Glide.with(context) - .load(backgroundImage) - .into(binding.backgroundImage) + .load(backgroundImage) + .into(binding.backgroundImage) } binding.promoText.setText(promoText) diff --git a/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt b/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt index 313e1c032..1cfb3f77a 100644 --- a/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt +++ b/app/src/main/java/com/automattic/loop/intro/IntroPagerTitleFragment.kt @@ -31,8 +31,8 @@ class IntroPagerTitleFragment : Fragment(R.layout.intro_title_template_view) { with(view) { Glide.with(context) - .load(backgroundImage) - .into(binding.backgroundImage) + .load(backgroundImage) + .into(binding.backgroundImage) } binding.titleText.setText(titleText) 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 df5a93bff..2112f5d7f 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerActivity.java @@ -33,7 +33,7 @@ import java.util.List; public class PhotoPickerActivity extends ViewBindingActivity - implements PhotoPickerFragment.PhotoPickerListener { + implements PhotoPickerFragment.PhotoPickerListener { private static final String PICKER_FRAGMENT_TAG = "picker_fragment_tag"; private static final String KEY_MEDIA_CAPTURE_PATH = "media_capture_path"; @@ -44,7 +44,7 @@ public class PhotoPickerActivity extends ViewBindingActivity mMediaCapturePath = mediaCapturePath); + mediaCapturePath -> mMediaCapturePath = mediaCapturePath); } private void launchWPStoriesCamera() { Intent intent = new Intent() - .putExtra(EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED, true); + .putExtra(EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED, true); setResult(RESULT_OK, intent); finish(); } @@ -231,13 +231,13 @@ private void launchWPStoriesCamera() { private void launchPictureLibrary() { // WPMediaUtils.launchPictureLibrary(this, false); startActivityForResult( - preparePictureLibraryIntent(getString(R.string.pick_photo), true), - RequestCodes.PICTURE_LIBRARY); + preparePictureLibraryIntent(getString(R.string.pick_photo), true), + RequestCodes.PICTURE_LIBRARY); } private void launchVideoLibrary() { startActivityForResult(prepareVideoLibraryIntent(getString(R.string.pick_video), true), - RequestCodes.VIDEO_LIBRARY); + RequestCodes.VIDEO_LIBRARY); } @@ -352,8 +352,8 @@ private void doMediaUrisSelected(@NonNull List mediaUris, @NonNull PhotoPic // }); // } else { Intent intent = new Intent() - .putExtra(EXTRA_MEDIA_URIS, convertUrisListToStringArray(mediaUris)) - .putExtra(EXTRA_MEDIA_SOURCE, source.name()); + .putExtra(EXTRA_MEDIA_URIS, convertUrisListToStringArray(mediaUris)) + .putExtra(EXTRA_MEDIA_SOURCE, source.name()); setResult(RESULT_OK, intent); finish(); // } @@ -361,8 +361,8 @@ private void doMediaUrisSelected(@NonNull List mediaUris, @NonNull PhotoPic private void doMediaIdSelected(long mediaId, @NonNull PhotoPickerMediaSource source) { Intent data = new Intent() - .putExtra(EXTRA_MEDIA_ID, mediaId) - .putExtra(EXTRA_MEDIA_SOURCE, source.name()); + .putExtra(EXTRA_MEDIA_ID, mediaId) + .putExtra(EXTRA_MEDIA_SOURCE, source.name()); setResult(RESULT_OK, data); finish(); } diff --git a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java index d5d417306..71392cb21 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerAdapter.java @@ -154,7 +154,7 @@ void setLoadThumbnails(boolean loadThumbnails) { @Override public ThumbnailViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ThumbnailViewHolder( - PhotoPickerThumbnailBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false) + PhotoPickerThumbnailBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false) ); } @@ -444,8 +444,8 @@ private long getVideoDuration(Uri videoUri) { try { mediaMetadataRetriever.setDataSource(mContext, videoUri); durationUs = Long.parseLong( - mediaMetadataRetriever.extractMetadata( - MediaMetadataRetriever.METADATA_KEY_DURATION)); + mediaMetadataRetriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_DURATION)); } catch (NumberFormatException e) { durationUs = -1; } catch (Exception ex) { @@ -499,11 +499,11 @@ private void addMedia(Uri baseUri, boolean isVideo) { Cursor cursor = null; try { cursor = mContext.getContentResolver().query( - baseUri, - projection, - null, - null, - null); + baseUri, + projection, + null, + null, + null); } catch (SecurityException e) { Log.e(TAG, e.getMessage()); } diff --git a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java index 90c9a77ec..b10b36088 100644 --- a/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java +++ b/app/src/main/java/com/automattic/loop/photopicker/PhotoPickerFragment.java @@ -123,7 +123,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.photo_picker_fragment, container, false); } @@ -232,7 +232,7 @@ public void doIconClicked(@NonNull PhotoPickerIcon icon) { || icon == PhotoPickerIcon.ANDROID_CAPTURE_VIDEO || icon == PhotoPickerIcon.WP_STORIES_CAPTURE) { if (ContextCompat.checkSelfPermission( - getActivity(), permission.CAMERA) != PackageManager.PERMISSION_GRANTED || !hasStoragePermission()) { + getActivity(), permission.CAMERA) != PackageManager.PERMISSION_GRANTED || !hasStoragePermission()) { // requestCameraPermission(); Toast.makeText(getActivity(), "Need permissions", Toast.LENGTH_SHORT).show(); return; @@ -507,7 +507,7 @@ public void onDestroyActionMode(ActionMode mode) { private boolean hasStoragePermission() { return ContextCompat.checkSelfPermission( - getActivity(), permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + getActivity(), permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } ///* diff --git a/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt b/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt index da9267ff0..31f3cb640 100644 --- a/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt +++ b/stories/src/main/java/com/wordpress/stories/ByActivityViewBinding.kt @@ -16,7 +16,7 @@ import kotlin.reflect.KProperty * https://gist.github.com/seanghay/0fd991d7a823815500557fe043b052ce#file-view-binding-ktx-kt */ fun AppCompatActivity.viewBinding(initializer: (LayoutInflater) -> T) = - ViewBindingPropertyDelegate(this, initializer) + ViewBindingPropertyDelegate(this, initializer) class ViewBindingPropertyDelegate( private val activity: AppCompatActivity, @@ -44,7 +44,7 @@ class ViewBindingPropertyDelegate( // This must be on the main thread only if (Looper.myLooper() != Looper.getMainLooper()) { throw IllegalThreadStateException( - "This cannot be called from other threads. It should be on the main thread only." + "This cannot be called from other threads. It should be on the main thread only." ) } diff --git a/stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt b/stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt index d7f647e08..58b0cdae6 100644 --- a/stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt +++ b/stories/src/main/java/com/wordpress/stories/ByFragmentViewBinding.kt @@ -15,7 +15,7 @@ import kotlin.reflect.KProperty * https://gist.github.com/Zhuinden/ea3189198938bd16c03db628e084a4fa#file-fragmentviewbindingdelegate-kt */ fun Fragment.viewBinding(initializer: (View) -> T) = - FragmentViewBindingDelegate(this, initializer) + FragmentViewBindingDelegate(this, initializer) class FragmentViewBindingDelegate( val fragment: Fragment, diff --git a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt index 34e976602..f57d2143b 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/story/StoryFrameSelectorAdapter.kt @@ -24,10 +24,10 @@ class StoryFrameSelectorAdapter : RecyclerView.Adapter StoryFrameHolderItem( - FragmentStoryFrameItemBinding.inflate( - LayoutInflater - .from(parent.context) - ) + FragmentStoryFrameItemBinding.inflate( + LayoutInflater + .from(parent.context) + ) ) else -> throw NotImplementedError("Unknown ViewType") } @@ -97,10 +97,10 @@ class StoryFrameSelectorAdapter : RecyclerView.Adapter Unit class TextColorPickerAdapter internal constructor(private val context: Context, mode: Mode, startColor: Int? = null) : - RecyclerView.Adapter() { + RecyclerView.Adapter() { private val colorPickerColors = getDefaultColors(context, mode) private var onTextColorPickerClickListener: OnTextColorPickerClickListener? = null @@ -74,10 +74,10 @@ class TextColorPickerAdapter internal constructor(private val context: Context, fun getDefaultColors(context: Context, mode: Mode): List { val baseArray = context.resources.obtainTypedArray( - when (mode) { - Mode.FOREGROUND -> R.array.text_colors - Mode.BACKGROUND -> R.array.text_background_colors - } + when (mode) { + Mode.FOREGROUND -> R.array.text_colors + Mode.BACKGROUND -> R.array.text_background_colors + } ) return baseArray.extractColorList(context.resources).also { baseArray.recycle() } diff --git a/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt b/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt index 3b84b961f..a575774af 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/text/TextEditorDialogFragment.kt @@ -215,11 +215,11 @@ class TextEditorDialogFragment : DialogFragment() { } binding.textAlignmentButton.setImageResource( - when (textAlignment) { - TextAlignment.LEFT -> R.drawable.ic_gridicons_align_left_32 - TextAlignment.CENTER -> R.drawable.ic_gridicons_align_center_32 - TextAlignment.RIGHT -> R.drawable.ic_gridicons_align_right_32 - } + when (textAlignment) { + TextAlignment.LEFT -> R.drawable.ic_gridicons_align_left_32 + TextAlignment.CENTER -> R.drawable.ic_gridicons_align_center_32 + TextAlignment.RIGHT -> R.drawable.ic_gridicons_align_right_32 + } ) // The background span needs to be re-applied to adjust for the alignment change @@ -278,8 +278,8 @@ class TextEditorDialogFragment : DialogFragment() { putString(EXTRA_INPUT_TEXT, inputText) putInt( - EXTRA_TEXT_COLOR_CODE, textStyler?.textColor - ?: ContextCompat.getColor(appCompatActivity, android.R.color.white) + EXTRA_TEXT_COLOR_CODE, textStyler?.textColor + ?: ContextCompat.getColor(appCompatActivity, android.R.color.white) ) putInt(EXTRA_TEXT_BACKGROUND_COLOR_CODE, textStyler?.textBackgroundColor ?: Color.TRANSPARENT) putInt(EXTRA_TEXT_ALIGNMENT, textStyler?.textAlignment ?: TextAlignment.default()) From f2804255e512242bcbe03a97eb32699aa91be57f Mon Sep 17 00:00:00 2001 From: faranjit Date: Tue, 26 Jan 2021 13:30:43 +0300 Subject: [PATCH 7/7] update from base repository --- .../compose/ComposeLoopFrameActivity.kt | 569 +++++++++--------- 1 file changed, 286 insertions(+), 283 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 61a219be8..7958ba6ba 100644 --- a/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt +++ b/stories/src/main/java/com/wordpress/stories/compose/ComposeLoopFrameActivity.kt @@ -131,9 +131,11 @@ enum class ScreenTouchBlockMode { BLOCK_TOUCH_MODE_NONE, BLOCK_TOUCH_MODE_FULL_SCREEN, // used when saving - user is not allowed to touch anything BLOCK_TOUCH_MODE_PHOTO_EDITOR_ERROR_PENDING_RESOLUTION, // used when in error resolution mode: user needs to take + // action, so we allow them to use the StoryFrameSelector and menu, but no edits on // the Photo Editor canvas are allowed at this stage BLOCK_TOUCH_MODE_PHOTO_EDITOR_READY, // used when errors have been sorted out by the user - no edits allowed, + // but they should be good to upload the Story now BLOCK_TOUCH_MODE_DELETE_SLIDE // Used in delete slide mode, tapping the screen releases the block } @@ -329,19 +331,19 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec ViewCompat.setOnApplyWindowInsetsListener(binding.composeLoopFrameLayout) { _, insets -> // set insetTop as margin to all controls appearing at the top of the screen addInsetTopMargin( - contentComposerBinding.nextButton.layoutParams, - nextButtonBaseTopMargin, - insets.systemWindowInsetTop + contentComposerBinding.nextButton.layoutParams, + nextButtonBaseTopMargin, + insets.systemWindowInsetTop ) addInsetTopMargin( - contentComposerBinding.closeButton.layoutParams, - topControlsBaseTopMargin, - insets.systemWindowInsetTop + contentComposerBinding.closeButton.layoutParams, + topControlsBaseTopMargin, + insets.systemWindowInsetTop ) addInsetTopMargin( - contentComposerBinding.controlFlashGroup.layoutParams, - topControlsBaseTopMargin, - insets.systemWindowInsetTop + contentComposerBinding.controlFlashGroup.layoutParams, + topControlsBaseTopMargin, + insets.systemWindowInsetTop ) bottomNavigationBarMargin = insets.systemWindowInsetBottom workingAreaRect = calculateWorkingArea() @@ -349,7 +351,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec contentComposerBinding.deleteView.addBottomOffset(bottomNavigationBarMargin) contentComposerBinding.deleteSlideView.addBottomOffset(bottomNavigationBarMargin) (supportFragmentManager.findFragmentById(R.id.bottom_strip_view) - as? StoryFrameSelectorFragment)?.setBottomOffset(bottomNavigationBarMargin) + as? StoryFrameSelectorFragment)?.setBottomOffset(bottomNavigationBarMargin) insets } @@ -361,11 +363,11 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec workingAreaRect = calculateWorkingArea() photoEditor = PhotoEditor.Builder(this, contentComposerBinding.photoEditorView) - .setPinchTextScalable(true) // set flag to make text scalable when pinch - .setDeleteView(contentComposerBinding.deleteView) - .setWorkAreaRect(workingAreaRect) - .setAuthenticatitonHeaderInterface(authHeaderInterfaceBridge) - .build() // build photo editor sdk + .setPinchTextScalable(true) // set flag to make text scalable when pinch + .setDeleteView(contentComposerBinding.deleteView) + .setWorkAreaRect(workingAreaRect) + .setAuthenticatitonHeaderInterface(authHeaderInterfaceBridge) + .build() // build photo editor sdk photoEditor.setOnPhotoEditorListener(object : OnPhotoEditorListener { override fun onEditTextChangeListener( @@ -383,9 +385,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec rootView.visibility = View.INVISIBLE val textEditorDialogFragment = TextEditorDialogFragment.show( - this@ComposeLoopFrameActivity, - text, - textStyler + this@ComposeLoopFrameActivity, + text, + textStyler ) textEditorDialogFragment.setOnTextEditorListener(object : TextEditorDialogFragment.TextEditor { override fun onDone(inputText: String, textStyler: TextStyler) { @@ -451,40 +453,40 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec }) backgroundSurfaceManager = BackgroundSurfaceManager( - savedInstanceState, - lifecycle, - contentComposerBinding.photoEditorView, - supportFragmentManager, - object : FlashSupportChangeListener { - override fun onFlashSupportChanged(isSupported: Boolean) { - if (isSupported) { - contentComposerBinding.cameraFlashButton.visibility = View.VISIBLE - contentComposerBinding.labelFlash.visibility = View.VISIBLE - } else { - contentComposerBinding.cameraFlashButton.visibility = View.INVISIBLE - contentComposerBinding.labelFlash.visibility = View.INVISIBLE - } + savedInstanceState, + lifecycle, + contentComposerBinding.photoEditorView, + supportFragmentManager, + object : FlashSupportChangeListener { + override fun onFlashSupportChanged(isSupported: Boolean) { + if (isSupported) { + contentComposerBinding.cameraFlashButton.visibility = View.VISIBLE + contentComposerBinding.labelFlash.visibility = View.VISIBLE + } else { + contentComposerBinding.cameraFlashButton.visibility = View.INVISIBLE + contentComposerBinding.labelFlash.visibility = View.INVISIBLE + } + } + }, + BuildConfig.USE_CAMERAX, + object : BackgroundSurfaceManagerReadyListener { + override fun onBackgroundSurfaceManagerReady() { + if (savedInstanceState == null && !firstIntentLoaded) { + onLoadFromIntent(intent) + firstIntentLoaded = true } - }, - BuildConfig.USE_CAMERAX, - object : BackgroundSurfaceManagerReadyListener { - override fun onBackgroundSurfaceManagerReady() { - if (savedInstanceState == null && !firstIntentLoaded) { - onLoadFromIntent(intent) - firstIntentLoaded = true - } - if (launchCameraRequestPending) { - launchCameraRequestPending = false - launchCameraPreviewWithSurfaceSafeguard() - } else if (launchVideoPlayerRequestPending) { - launchVideoPlayerRequestPending = false - showPlayVideoWithSurfaceSafeguard(launchVideoPlayerRequestPendingSource) - } + if (launchCameraRequestPending) { + launchCameraRequestPending = false + launchCameraPreviewWithSurfaceSafeguard() + } else if (launchVideoPlayerRequestPending) { + launchVideoPlayerRequestPending = false + showPlayVideoWithSurfaceSafeguard(launchVideoPlayerRequestPendingSource) } - }, - authHeaderInterfaceBridge, - useTempCaptureFile + } + }, + authHeaderInterfaceBridge, + useTempCaptureFile ) lifecycle.addObserver(backgroundSurfaceManager) @@ -508,8 +510,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec storyIndexToSelect = getStoryIndexFromIntentOrBundle(savedInstanceState, intent) storyViewModel = ViewModelProvider( - this, - StoryViewModelFactory(StoryRepository, storyIndexToSelect) + this, + StoryViewModelFactory(StoryRepository, storyIndexToSelect) )[StoryViewModel::class.java] // request the BackgroundSurfaceManager to prime the textureView so it's ready when needed. @@ -517,12 +519,12 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // check camera selection, flash state from preferences CameraSelection.valueOf( - getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_camera_selection), 0) + getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_camera_selection), 0) )?.let { cameraSelection = it } FlashIndicatorState.valueOf( - getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_flash_mode_selection), 0) + getPreferences(Context.MODE_PRIVATE).getInt(getString(R.string.pref_flash_mode_selection), 0) )?.let { flashModeSelection = it } @@ -538,7 +540,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec if (savedInstanceState != null) { currentOriginalCapturedFile = - savedInstanceState.getSerializable(STATE_KEY_CURRENT_ORIGINAL_CAPTURED_FILE) as File? + savedInstanceState.getSerializable(STATE_KEY_CURRENT_ORIGINAL_CAPTURED_FILE) as File? preHookRun = savedInstanceState.getBoolean(STATE_KEY_PREHOOK_RUN) firstIntentLoaded = savedInstanceState.getBoolean(STATE_KEY_FIRST_INTENT_LOADED) @@ -546,7 +548,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec storyViewModel.replaceCurrentStory( StorySerializerUtils.deserializeStory( - requireNotNull(savedInstanceState.getString(STATE_KEY_STORY_SAVE_STATE)) + requireNotNull(savedInstanceState.getString(STATE_KEY_STORY_SAVE_STATE)) ) ) @@ -563,7 +565,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec super.onStart() val selectedFrameIndex = storyViewModel.getSelectedFrameIndex() if (!launchCameraRequestPending && !launchVideoPlayerRequestPending && - selectedFrameIndex < storyViewModel.getCurrentStorySize()) { + selectedFrameIndex < storyViewModel.getCurrentStorySize()) { updateBackgroundSurfaceUIWithStoryFrame(selectedFrameIndex) } // upon loading an existing Story, show the generic announcement dialog if present @@ -579,7 +581,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // 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) { + firstIntentLoaded && !permissionsRequestForCameraInProgress) { // finally, delete the captured media deleteCapturedMedia() finish() @@ -639,8 +641,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // select the first errored frame onStoryFrameSelected( - oldIndex = StoryRepository.DEFAULT_FRAME_NONE_SELECTED, - newIndex = minIndexToSelect!!.frameIndex + oldIndex = StoryRepository.DEFAULT_FRAME_NONE_SELECTED, + newIndex = minIndexToSelect!!.frameIndex ) // show dialog @@ -651,9 +653,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val errorDialogTitle = String.format(stringSingularOrPlural, errors.size) FrameSaveErrorDialog.newInstance( - errorDialogTitle, - getString(R.string.dialog_story_saving_error_message), - getString(android.R.string.ok) + errorDialogTitle, + getString(R.string.dialog_story_saving_error_message), + getString(android.R.string.ok) ).show(supportFragmentManager, FRAGMENT_DIALOG) } @@ -662,22 +664,22 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val intent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) if (intent.resolveActivity(packageManager) != null) { FrameSaveErrorDialog.newInstance( - title = getString(R.string.dialog_insufficient_device_storage_error_title), - message = getString(R.string.dialog_insufficient_device_storage_error_message), - okButtonLabel = getString(R.string.dialog_insufficient_device_storage_error_ok_button), - listener = object : FrameSaveErrorDialogOk { - override fun OnOkClicked(dialog: DialogFragment) { - dialog.dismiss() - val settingsIntent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) - if (settingsIntent.resolveActivity(packageManager) != null) { - startActivity(settingsIntent) - } + title = getString(R.string.dialog_insufficient_device_storage_error_title), + message = getString(R.string.dialog_insufficient_device_storage_error_message), + okButtonLabel = getString(R.string.dialog_insufficient_device_storage_error_ok_button), + listener = object : FrameSaveErrorDialogOk { + override fun OnOkClicked(dialog: DialogFragment) { + dialog.dismiss() + val settingsIntent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) + if (settingsIntent.resolveActivity(packageManager) != null) { + startActivity(settingsIntent) } - }).show(supportFragmentManager, FRAGMENT_DIALOG) + } + }).show(supportFragmentManager, FRAGMENT_DIALOG) } else { FrameSaveErrorDialog.newInstance( - title = getString(R.string.dialog_insufficient_device_storage_error_title), - message = getString(R.string.dialog_insufficient_device_storage_error_message) + title = getString(R.string.dialog_insufficient_device_storage_error_title), + message = getString(R.string.dialog_insufficient_device_storage_error_message) ).show(supportFragmentManager, FRAGMENT_DIALOG) } } @@ -692,13 +694,13 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec protected open fun onLoadFromIntent(intent: Intent) { val partialCameraOperationInProgress = intent.hasExtra(requestCodes.EXTRA_LAUNCH_WPSTORIES_CAMERA_REQUESTED) || - permissionsRequestForCameraInProgress + permissionsRequestForCameraInProgress if (storyViewModel.getCurrentStoryIndex() == StoryRepository.DEFAULT_NONE_SELECTED) { storyViewModel.loadStory(storyIndexToSelect) storyIndexToSelect = storyViewModel.getCurrentStoryIndex() } else if (!partialCameraOperationInProgress && storyIndexToSelect != StoryRepository.DEFAULT_NONE_SELECTED && - StoryRepository.getStoryAtIndex(storyIndexToSelect).frames.isNotEmpty()) { + StoryRepository.getStoryAtIndex(storyIndexToSelect).frames.isNotEmpty()) { storyViewModel.loadStory(storyIndexToSelect) showGenericAnnouncementDialogWhenReady = true return @@ -710,7 +712,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } 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()) { + StoryRepository.getStoryAtIndex(storySaveResult.storyIndex).frames.isNotEmpty()) { // dismiss the error notification intent.action?.let { val notificationManager = NotificationManagerCompat.from(this) @@ -728,7 +730,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } } else if (intent.hasExtra(requestCodes.EXTRA_MEDIA_URIS)) { val uriList: List = convertStringArrayIntoUrisList( - intent.getStringArrayExtra(requestCodes.EXTRA_MEDIA_URIS) + intent.getStringArrayExtra(requestCodes.EXTRA_MEDIA_URIS) ) addFramesToStoryFromMediaUriList(uriList) setDefaultSelectionAndUpdateBackgroundSurfaceUI(uriList) @@ -768,8 +770,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // save Story slide (frame) state addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) outState.putString( - STATE_KEY_STORY_SAVE_STATE, StorySerializerUtils.serializeStory( - storyViewModel.getStoryAtIndex(storyViewModel.getCurrentStoryIndex()) + STATE_KEY_STORY_SAVE_STATE, StorySerializerUtils.serializeStory( + storyViewModel.getStoryAtIndex(storyViewModel.getCurrentStoryIndex()) ) ) outState.putInt(STATE_KEY_STORY_SAVE_STATE_SELECTED_FRAME, storyViewModel.getSelectedFrameIndex()) @@ -827,7 +829,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val providerHandlesMediaPickerResult = mediaPickerProvider?.providerHandlesOnActivityResult() ?: false if (data.hasExtra(requestCodes.EXTRA_MEDIA_URIS) && !providerHandlesMediaPickerResult) { val uriList: List = convertStringArrayIntoUrisList( - data.getStringArrayExtra(requestCodes.EXTRA_MEDIA_URIS) + data.getStringArrayExtra(requestCodes.EXTRA_MEDIA_URIS) ) addFramesToStoryFromMediaUriList(uriList) setDefaultSelectionAndUpdateBackgroundSurfaceUI(uriList) @@ -869,12 +871,12 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec protected fun addFrameToStoryFromMediaUri(mediaUri: Uri) { storyViewModel - .addStoryFrameItemToCurrentStory( - StoryFrameItem( - UriBackgroundSource(contentUri = mediaUri), - frameItemType = if (isVideo(mediaUri.toString())) VIDEO() else IMAGE - ) + .addStoryFrameItemToCurrentStory( + StoryFrameItem( + UriBackgroundSource(contentUri = mediaUri), + frameItemType = if (isVideo(mediaUri.toString())) VIDEO() else IMAGE ) + ) } private fun updateBackgroundSurfaceUIWithStoryFrame( @@ -887,82 +889,83 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun addClickListeners() { contentComposerBinding.run { cameraCaptureButton - .setOnTouchListener( - PressAndHoldGestureHelper( - PressAndHoldGestureHelper.CLICK_LENGTH, - object : PressAndHoldGestureListener { - override fun onClickGesture() { - if (cameraOperationInCourse) { - showToast(getString(R.string.toast_capture_operation_in_progress)) - return - } - timesUpHandler.removeCallbacksAndMessages(null) - takeStillPicture() - } - - override fun onHoldingGestureStart() { - timesUpHandler.removeCallbacksAndMessages(null) - if (PermissionUtils.allVideoPermissionsGranted( - this@ComposeLoopFrameActivity) - ) { - // if we at least have - startRecordingVideoAfterVibrationIndication() - } else { - // request permissions including audio for video - val permissionName = - PermissionUtils.anyVideoNeededPermissionPermanentlyDenied( - this@ComposeLoopFrameActivity - ) - - permissionName?.let { - showPermissionPermanentlyDeniedDialog(it) - } - ?: PermissionUtils - .requestAllRequiredPermissionsIncludingAudioForVideo( - this@ComposeLoopFrameActivity - ).also { - permissionsRequestForCameraInProgress = true - } + .setOnTouchListener( + PressAndHoldGestureHelper( + PressAndHoldGestureHelper.CLICK_LENGTH, + object : PressAndHoldGestureListener { + override fun onClickGesture() { + if (cameraOperationInCourse) { + showToast(getString(R.string.toast_capture_operation_in_progress)) + return + } + timesUpHandler.removeCallbacksAndMessages(null) + takeStillPicture() + } + + override fun onHoldingGestureStart() { + timesUpHandler.removeCallbacksAndMessages(null) + if (PermissionUtils.allVideoPermissionsGranted( + this@ComposeLoopFrameActivity + ) + ) { + // if we at least have + startRecordingVideoAfterVibrationIndication() + } else { + // request permissions including audio for video + val permissionName = + PermissionUtils.anyVideoNeededPermissionPermanentlyDenied( + this@ComposeLoopFrameActivity + ) + + permissionName?.let { + showPermissionPermanentlyDeniedDialog(it) + } + ?: PermissionUtils + .requestAllRequiredPermissionsIncludingAudioForVideo( + this@ComposeLoopFrameActivity + ).also { + permissionsRequestForCameraInProgress = true } - } + } + } - override fun onHoldingGestureEnd() { - stopRecordingVideo(false) - } + override fun onHoldingGestureEnd() { + stopRecordingVideo(false) + } - override fun onHoldingGestureCanceled() { - stopRecordingVideo(true) - } + override fun onHoldingGestureCanceled() { + stopRecordingVideo(true) + } - override fun onStartDetectionWait() { - if (cameraOperationInCourse) { - showToast(getString(R.string.toast_capture_operation_in_progress)) - return - } - // when the wait to see whether this is a "press and hold" gesture starts, - // start the animation to grow the capture button radius - cameraCaptureButton - .animate() - .scaleXBy(0.3f) // scale up by 30% - .scaleYBy(0.3f) - .duration = PressAndHoldGestureHelper.CLICK_LENGTH - } - - override fun onTouchEventDetectionEnd() { - if (cameraOperationInCourse) { - return - } - // when gesture detection ends, we're good to - // get the capture button shape as it originally was (idle state) - cameraCaptureButton.clearAnimation() - cameraCaptureButton - .animate() - .scaleX(1.0f) - .scaleY(1.0f) - .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 - } - }) - ) + override fun onStartDetectionWait() { + if (cameraOperationInCourse) { + showToast(getString(R.string.toast_capture_operation_in_progress)) + return + } + // when the wait to see whether this is a "press and hold" gesture starts, + // start the animation to grow the capture button radius + cameraCaptureButton + .animate() + .scaleXBy(0.3f) // scale up by 30% + .scaleYBy(0.3f) + .duration = PressAndHoldGestureHelper.CLICK_LENGTH + } + + override fun onTouchEventDetectionEnd() { + if (cameraOperationInCourse) { + return + } + // when gesture detection ends, we're good to + // get the capture button shape as it originally was (idle state) + cameraCaptureButton.clearAnimation() + cameraCaptureButton + .animate() + .scaleX(1.0f) + .scaleY(1.0f) + .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 + } + }) + ) containerGalleryUpload.setOnClickListener { showMediaPicker() @@ -998,18 +1001,18 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec discardOkButton = getString(R.string.dialog_discard_story_ok_button_edit) } FrameSaveErrorDialog.newInstance( - title = discardTitle, - message = discardMessage, - okButtonLabel = discardOkButton, - listener = object : FrameSaveErrorDialogOk { - override fun OnOkClicked(dialog: DialogFragment) { - addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) - dialog.dismiss() - // discard the whole story - safelyDiscardCurrentStoryAndCleanUpIntent() - storyDiscardListener?.onStoryDiscarded() - } - }).show(supportFragmentManager, FRAGMENT_DIALOG) + title = discardTitle, + message = discardMessage, + okButtonLabel = discardOkButton, + listener = object : FrameSaveErrorDialogOk { + override fun OnOkClicked(dialog: DialogFragment) { + addCurrentViewsToFrameAtIndex(storyViewModel.getSelectedFrameIndex()) + dialog.dismiss() + // discard the whole story + safelyDiscardCurrentStoryAndCleanUpIntent() + storyDiscardListener?.onStoryDiscarded() + } + }).show(supportFragmentManager, FRAGMENT_DIALOG) } } } @@ -1049,29 +1052,29 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } // show dialog FrameSaveErrorDialog.newInstance( - title = getString(R.string.dialog_discard_page_title), - message = messageToUse, - okButtonLabel = getString(R.string.dialog_discard_page_ok_button), - listener = object : FrameSaveErrorDialogOk { - override fun OnOkClicked(dialog: DialogFragment) { - dialog.dismiss() - if (storyViewModel.getCurrentStorySize() == 1) { - // discard the whole story - safelyDiscardCurrentStoryAndCleanUpIntent() - storyDiscardListener?.onStoryDiscarded() - } else { - // get currentFrame value as it will change after calling onAboutToDeleteStoryFrame - val currentFrameToDeleteIndex = storyViewModel.getSelectedFrameIndex() - onAboutToDeleteStoryFrame(currentFrameToDeleteIndex) - storyDiscardListener?.onFrameRemove( - storyViewModel.getCurrentStoryIndex(), - currentFrameToDeleteIndex - ) - // now discard it from the viewModel - storyViewModel.removeFrameAt(currentFrameToDeleteIndex) - } + title = getString(R.string.dialog_discard_page_title), + message = messageToUse, + okButtonLabel = getString(R.string.dialog_discard_page_ok_button), + listener = object : FrameSaveErrorDialogOk { + override fun OnOkClicked(dialog: DialogFragment) { + dialog.dismiss() + if (storyViewModel.getCurrentStorySize() == 1) { + // discard the whole story + safelyDiscardCurrentStoryAndCleanUpIntent() + storyDiscardListener?.onStoryDiscarded() + } else { + // get currentFrame value as it will change after calling onAboutToDeleteStoryFrame + val currentFrameToDeleteIndex = storyViewModel.getSelectedFrameIndex() + onAboutToDeleteStoryFrame(currentFrameToDeleteIndex) + storyDiscardListener?.onFrameRemove( + storyViewModel.getCurrentStoryIndex(), + currentFrameToDeleteIndex + ) + // now discard it from the viewModel + storyViewModel.removeFrameAt(currentFrameToDeleteIndex) } - }).show(supportFragmentManager, FRAGMENT_DIALOG) + } + }).show(supportFragmentManager, FRAGMENT_DIALOG) disableDeleteSlideMode() } } @@ -1101,7 +1104,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun anyOfOriginalIntentResultsIsError(): Boolean { if (intent.hasExtra(KEY_STORY_SAVE_RESULT)) { val storySaveResult = - intent.getParcelableExtra(KEY_STORY_SAVE_RESULT) as StorySaveResult? + intent.getParcelableExtra(KEY_STORY_SAVE_RESULT) as StorySaveResult? storySaveResult?.let { // where there any errors when we opened the Activity to handle those errors? return storySaveResult.isSuccess() @@ -1162,7 +1165,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec checkForLowSpaceAndShowDialog() } storyViewModel.updateCurrentSelectedFrameOnRetryResult( - result.frameSaveResult[0] + result.frameSaveResult[0] ) // need to do this so AddedViews get properly placed on the PhotoEditor refreshStoryFrameSelection() @@ -1335,19 +1338,19 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec override fun onImageSaved(file: File) { runOnUiThread { Glide.with(this@ComposeLoopFrameActivity) - .load(file) - .transform(CenterCrop(), RoundedCorners(16)) - .into(galleryUploadImg) + .load(file) + .transform(CenterCrop(), RoundedCorners(16)) + .into(galleryUploadImg) Glide.with(this@ComposeLoopFrameActivity) - .load(file) - .transform(CenterCrop()) - .into(photoEditorView.source) + .load(file) + .transform(CenterCrop()) + .into(photoEditorView.source) storyViewModel.apply { addStoryFrameItemToCurrentStory( - StoryFrameItem( - FileBackgroundSource(file = file), - frameItemType = StoryFrameItemType.IMAGE - ) + StoryFrameItem( + FileBackgroundSource(file = file), + frameItemType = StoryFrameItemType.IMAGE + ) ) setSelectedFrame(storyViewModel.getLastFrameIndexInCurrentStory()) } @@ -1396,10 +1399,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec runOnUiThread { storyViewModel.apply { addStoryFrameItemToCurrentStory( - StoryFrameItem( - FileBackgroundSource(file = it), - frameItemType = VIDEO() - ) + StoryFrameItem( + FileBackgroundSource(file = it), + frameItemType = VIDEO() + ) ) setSelectedFrame(storyViewModel.getLastFrameIndexInCurrentStory()) } @@ -1426,9 +1429,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // Vibrate for 100 milliseconds if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { vibrator.vibrate( - VibrationEffect.createOneShot( - VIBRATION_INDICATION_LENGTH_MS, VibrationEffect.DEFAULT_AMPLITUDE - ) + VibrationEffect.createOneShot( + VIBRATION_INDICATION_LENGTH_MS, VibrationEffect.DEFAULT_AMPLITUDE + ) ) } else { // deprecated in API 26 @@ -1448,10 +1451,10 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec cameraCaptureButton.stopProgressingAnimation() cameraCaptureButton.clearAnimation() cameraCaptureButton - .animate() - .scaleX(1.0f) - .scaleY(1.0f) - .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 + .animate() + .scaleX(1.0f) + .scaleY(1.0f) + .duration = PressAndHoldGestureHelper.CLICK_LENGTH / 4 backgroundSurfaceManager.stopRecordingVideo() showVideoUIControls() } @@ -1474,44 +1477,44 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec file.createNewFile() photoEditor.saveVideoAsFile( - inputFile, - file.absolutePath, - storyViewModel.isSelectedFrameAudioMuted(), - object : PhotoEditor.OnSaveWithCancelAndProgressListener { - override fun onProgress(progress: Double) { - // TODO implement progress - } + inputFile, + file.absolutePath, + storyViewModel.isSelectedFrameAudioMuted(), + object : PhotoEditor.OnSaveWithCancelAndProgressListener { + override fun onProgress(progress: Double) { + // TODO implement progress + } - override fun onCancel(noAddedViews: Boolean) { - runOnUiThread { - hideLoading() - showSnackbar("No views added - original video saved") - } + override fun onCancel(noAddedViews: Boolean) { + runOnUiThread { + hideLoading() + showSnackbar("No views added - original video saved") } + } - override fun onSuccess(filePath: String) { - runOnUiThread { - hideLoading() - deleteCapturedMedia() - photoEditor.clearAllViews() - sendNewStoryFrameReadyBroadcast(file) - showSnackbar( - getString(R.string.label_snackbar_loop_frame_saved), - getString(R.string.label_snackbar_share), - OnClickListener { shareAction(file) } - ) - hideEditModeUIControls() - switchCameraPreviewOn() - } + override fun onSuccess(filePath: String) { + runOnUiThread { + hideLoading() + deleteCapturedMedia() + photoEditor.clearAllViews() + sendNewStoryFrameReadyBroadcast(file) + showSnackbar( + getString(R.string.label_snackbar_loop_frame_saved), + getString(R.string.label_snackbar_share), + OnClickListener { shareAction(file) } + ) + hideEditModeUIControls() + switchCameraPreviewOn() } + } - override fun onFailure(exception: Exception) { - runOnUiThread { - hideLoading() - showSnackbar("Failed to save Video") - } + override fun onFailure(exception: Exception) { + runOnUiThread { + hideLoading() + showSnackbar("Failed to save Video") } - }) + } + }) } catch (e: IOException) { e.printStackTrace() hideLoading() @@ -1531,30 +1534,30 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec file.createNewFile() val saveSettings = SaveSettings.Builder() - .setClearViewsEnabled(true) - .setTransparencyEnabled(true) - .build() + .setClearViewsEnabled(true) + .setTransparencyEnabled(true) + .build() photoEditor.saveVideoFromStaticBackgroundAsFile( - file.absolutePath, - saveSettings, - object : PhotoEditor.OnSaveWithCancelListener { - override fun onCancel(noAddedViews: Boolean) { - // TODO not implemented - } + file.absolutePath, + saveSettings, + object : PhotoEditor.OnSaveWithCancelListener { + override fun onCancel(noAddedViews: Boolean) { + // TODO not implemented + } - override fun onSuccess(filePath: String) { - // now save the video with emoji, but using the previously saved video as input - hideLoading() - saveVideo(Uri.parse(filePath)) - // TODO: delete the temporal video produced originally - } + override fun onSuccess(filePath: String) { + // now save the video with emoji, but using the previously saved video as input + hideLoading() + saveVideo(Uri.parse(filePath)) + // TODO: delete the temporal video produced originally + } - override fun onFailure(exception: Exception) { - hideLoading() - showSnackbar("Failed to save Video") - } - }) + override fun onFailure(exception: Exception) { + hideLoading() + showSnackbar("Failed to save Video") + } + }) } catch (e: IOException) { e.printStackTrace() hideLoading() @@ -1585,7 +1588,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun showPermissionPermanentlyDeniedDialog(permission: String) { permissionDenialDialogProvider?.showPermissionPermanentlyDeniedDialog(permission) - ?: showToast(getString(R.string.toast_capture_operation_permission_needed)) + ?: showToast(getString(R.string.toast_capture_operation_permission_needed)) } private fun showToast(message: String) { @@ -1691,7 +1694,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val currentlyErrored = storyViewModel.anyOfCurrentStoryFramesIsErrored() val storyFrameSelectorFragment = supportFragmentManager.findFragmentById(R.id.bottom_strip_view) - as? StoryFrameSelectorFragment + as? StoryFrameSelectorFragment contentComposerBinding.run { when { // if we were in an error-handling situation but now all pages are OK we're ready to go @@ -1769,8 +1772,8 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec private fun shareAction(mediaFile: File) { val apkURI = FileProvider.getUriForFile( - this, - applicationContext.packageName + ".provider", mediaFile + this, + applicationContext.packageName + ".provider", mediaFile ) val shareIntent: Intent = Intent().apply { action = Intent.ACTION_SEND @@ -1797,9 +1800,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // but otherwise other apps will not be able to access our images unless we // scan them using [MediaScannerConnection] val mimeType = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(mediaFile.extension) + .getMimeTypeFromExtension(mediaFile.extension) MediaScannerConnection.scanFile( - this, arrayOf(mediaFile.absolutePath), arrayOf(mimeType), null + this, arrayOf(mediaFile.absolutePath), arrayOf(mimeType), null ) } @@ -1822,7 +1825,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec val arrayOfPaths = arrayOfNulls(mediaFileList.size) for ((index, mediaFile) in mediaFileList.withIndex()) { arrayOfmimeTypes[index] = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(mediaFile.extension) + .getMimeTypeFromExtension(mediaFile.extension) arrayOfPaths[index] = mediaFile.absolutePath } @@ -1830,7 +1833,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec // but otherwise other apps will not be able to access our images unless we // scan them using [MediaScannerConnection] MediaScannerConnection.scanFile( - this, arrayOfPaths, arrayOfmimeTypes, null + this, arrayOfPaths, arrayOfmimeTypes, null ) } @@ -1850,7 +1853,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec translucentView.visibility = View.GONE translucentErrorView.visibility = View.VISIBLE translucentErrorView.background = ColorDrawable( - ContextCompat.getColor(root.context, R.color.black_transp_error_scrim) + ContextCompat.getColor(root.context, R.color.black_transp_error_scrim) ) translucentErrorView.setOnTouchListener { _, _ -> // no op @@ -1862,7 +1865,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec translucentView.visibility = View.GONE translucentErrorView.visibility = View.VISIBLE translucentErrorView.background = ColorDrawable( - ContextCompat.getColor(root.context, android.R.color.transparent) + ContextCompat.getColor(root.context, android.R.color.transparent) ) translucentErrorView.setOnTouchListener { _, _ -> // no op @@ -1874,7 +1877,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec translucentView.visibility = View.GONE translucentErrorView.visibility = View.VISIBLE translucentErrorView.background = ColorDrawable( - ContextCompat.getColor(root.context, android.R.color.transparent) + ContextCompat.getColor(root.context, android.R.color.transparent) ) translucentErrorView.setOnTouchListener { _, _ -> // If the error view is tapped, dismiss it and cancel delete slide mode @@ -1970,9 +1973,9 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } else { val model = (source as? FileBackgroundSource)?.file ?: (source as UriBackgroundSource).contentUri Glide.with(this@ComposeLoopFrameActivity) - .load(model) - .transform(CenterCrop()) - .into(contentComposerBinding.photoEditorView.source) + .load(model) + .transform(CenterCrop()) + .into(contentComposerBinding.photoEditorView.source) showStaticBackground() } @@ -2074,7 +2077,7 @@ abstract class ComposeLoopFrameActivity : AppCompatActivity(), OnStoryFrameSelec } return false } else if (e2.y - e1.y > SWIPE_MIN_DISTANCE && - abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { + abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { // Top to bottom return false }