I try to study overlay library for kotlin applications https://github.com/KoderLabs/overlay-service. Right now I have a problem related with opening a new activity from a button which is located inside a FrameLayout class.
So, the task is push on button on Main Activity -> open new overlay small window and roll up Main Activity -> push on button inside a small window -> open new Activity.
In this code nothing happens after the clicking on button.
The project include main 3 classes:
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_simple_pip.setOnClickListener {
checkDrawOverlayPermission(IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE)
finishAffinity()
}
}
private fun checkDrawOverlayPermission(code: Int) {
if (Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
startActivityForResult(intent, IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE)
} else {
openFloatingWindow(code)
}
} else {
openFloatingWindow(code)
}
}
private fun openFloatingWindow(code: Int) {
when (code) {
IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE -> {
val intent = Intent(this, ImplementPipOverlayService::class.java)
val videoUrl =
"https://s3.amazonaws.com/data.development.momentpin.com/2019/7/3/1562152168485485-0661a550-9d83-11e9-9028-d7af09cf782e.mp4"
val notificationTitle = "Pip Overlay"
val notificationDescription = "Pip overlay description"
val notificationIcon = R.drawable.ic_launcher_foreground
val closeBtnColor = android.R.color.black
val closeBtnBgColor = android.R.color.transparent
intent.putExtra(ImplementPipOverlayService.KEY_STRING_VIDEO_URL, videoUrl)
intent.putExtra(ImplementPipOverlayService.KEY_STRING_NOTIFICATION_DESCRIPTION, notificationDescription)
intent.putExtra(ImplementPipOverlayService.KEY_STRING_NOTIFICATION_TITLE, notificationTitle)
intent.putExtra(ImplementPipOverlayService.KEY_INT_NOTIFICATION_ICON, notificationIcon)
intent.putExtra(ImplementPipOverlayService.KEY_INT_CLOSE_BUTTON_COLOR, closeBtnColor)
intent.putExtra(ImplementPipOverlayService.KEY_INT_CLOSE_BUTTON_BG_COLOR, closeBtnBgColor)
ContextCompat.startForegroundService(this, intent)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE, RESIZEABLE_CUSTOM_WEB_OVERLAY_REQUEST_CODE,
PIP_OVERLAY_REQUEST_CODE, RESIZEABLE_CUSTOM_VIDEO_OVERLAY_REQUEST_CODE -> {
if (Build.VERSION.SDK_INT >= 23) {
if (Settings.canDrawOverlays(this)) {
openFloatingWindow(requestCode)
}
} else {
openFloatingWindow(requestCode)
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
companion object {
const val IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE = 251
const val PIP_OVERLAY_REQUEST_CODE = 252
const val RESIZEABLE_CUSTOM_VIDEO_OVERLAY_REQUEST_CODE = 253
const val RESIZEABLE_CUSTOM_WEB_OVERLAY_REQUEST_CODE = 254
}
ImplementPipOverlayService
class ImplementPipOverlayService : PipOverlayService() {
var videoUrl: String? = null
var notificationTitle: String? = null
var notificationDescription: String? = null
var notificationIcon: Int? = null
var closeButtonColor: Int? = null
var closeButtonBg: Int? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
videoUrl = intent?.getStringExtra(KEY_STRING_VIDEO_URL)
notificationTitle = intent?.getStringExtra(KEY_STRING_NOTIFICATION_TITLE)
notificationDescription = intent?.getStringExtra(KEY_STRING_NOTIFICATION_DESCRIPTION)
notificationIcon = intent?.getIntExtra(KEY_INT_NOTIFICATION_ICON, -1)
closeButtonColor = intent?.getIntExtra(KEY_INT_CLOSE_BUTTON_COLOR, -1)
closeButtonBg = intent?.getIntExtra(KEY_INT_CLOSE_BUTTON_BG_COLOR, -1)
return super.onStartCommand(intent, flags, startId)
}
override fun getForegroundNotification(): Notification {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel("my_service", "My Background Service")
} else {
packageName
}
var notificationBuilder = NotificationCompat.Builder(this, channelId)
notificationBuilder = notificationTitle?.let {
notificationBuilder.setContentTitle(it)
} ?: run {
notificationBuilder.setContentTitle("Title")
}
notificationDescription?.let {
notificationBuilder = notificationBuilder.setContentText(it)
}
notificationIcon?.let {
notificationBuilder = notificationBuilder.setSmallIcon(it)
}
val notification: Notification = notificationBuilder.build()
return notification
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String {
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
}
override fun getInitialWindowSize(): Point {
return Point(100.toDp(), 100.toDp())
}
override fun getCustomLayoutId(): Int {
return R.layout.pip_layout
}
override fun onServiceRun() {
setOnEventListener(onFullscreen = {
// Not implemented
}, onClosed = {
// Not implemented
})
pipView.removeFullscreenButton()
closeButtonColor?.let {
pipView.getCloseButton().setColorFilter(it)
}
closeButtonBg?.let {
pipView.getCloseButton().setBackgroundColor(it)
}
}
companion object {
const val KEY_STRING_VIDEO_URL = "video_url"
const val KEY_INT_CLOSE_BUTTON_COLOR = "close_button_color"
const val KEY_INT_CLOSE_BUTTON_BG_COLOR = "close_button_background"
const val KEY_STRING_NOTIFICATION_TITLE = "notification_title"
const val KEY_STRING_NOTIFICATION_DESCRIPTION = "notification_description"
const val KEY_INT_NOTIFICATION_ICON = "notification_icon"
}
OverlayPipCustomView
class OverlayPipCustomView : FrameLayout {
private lateinit var constraintsRoot: ConstraintLayout
private lateinit var imageFullscreenButton: ImageView
private lateinit var imageCloseButton: ImageView
private lateinit var customLayoutContent: FrameLayout
private lateinit var customView: View
private lateinit var touchView: View
private lateinit var button: Button
private var playerViewSize: Int = 0
private var sizeChangeable: Boolean = true
private var playerType: Int = 0
private var haveFullscreen = true
/**
* Tracks if viewResizeable is fullscreen.
*/
private var fullscreenOn: Boolean = false
val isDraggable: Boolean
get() {
return !fullscreenOn
}
private var onFullscreen: () -> Unit = {}
private var onClosed: () -> Unit = {}
private var canHideActionButtons = true
private val hideActionHandler = Handler()
private val HIDE_ACTION_DURATION = 2000L
private val hideActionRunnable = Runnable {
if (!isMoving) {
hideActions()
}
}
var isMoving: Boolean = false
constructor(ctx: Context) : super(ctx) {
inflate(context, R.layout.layout_pip_custom_view, this)
initView()
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
setAttributes(attrs)
inflate(context, R.layout.layout_pip_custom_view, this)
initView()
}
constructor(ctx: Context, attrs: AttributeSet, defStyle: Int) : super(ctx, attrs, defStyle) {
setAttributes(attrs)
inflate(context, R.layout.layout_pip_custom_view, this)
initView()
}
private fun setAttributes(attrs: AttributeSet) {
context.theme.obtainStyledAttributes(
attrs,
R.styleable.OverlayCustomView,
0, 0).apply {
try {
playerViewSize = getInteger(R.styleable.OverlayCustomView_ov_player_size, 0)
playerType = getInteger(R.styleable.OverlayCustomView_ov_size_changeable, 0)
sizeChangeable = getBoolean(R.styleable.OverlayCustomView_ov_size_changeable, true)
} finally {
recycle()
}
}
doOnLayout {
startHideAction()
}
}
private fun initView() {
button = findViewById(R.id.button)
constraintsRoot = findViewById(R.id.constraints_root)
imageFullscreenButton = findViewById(R.id.image_screen_action)
imageCloseButton = findViewById(R.id.image_close)
customLayoutContent = findViewById(R.id.custom_view)
touchView = findViewById(R.id.touch_view)
setListeners()
}
fun addCustomView(view: View) {
customLayoutContent.addView(view)
}
fun addCustomView(layoutId: Int) {
customView = inflate(context, layoutId, null)
customLayoutContent.addView(customView)
}
fun getCloseButton() = imageCloseButton
fun getConstraintsRoot() = constraintsRoot
fun getCustomLayoutContent() = customLayoutContent
fun getCustomView() = customView
fun getFullscreenButton() = imageFullscreenButton
fun getTouchView() = touchView
fun removeFullscreenButton() {
haveFullscreen = false
imageFullscreenButton.invisible()
}
private fun setListeners() {
imageFullscreenButton.setOnClickListener {
onFullscreen.invoke()
}
button.setOnClickListener{
println("pressed")
fun alert(context: Context, text: String) {
val intent = Intent(context, MainActivity2::class.java)
intent.putExtra("text", text)
context.startActivity(intent)
}
}
imageCloseButton.setOnClickListener {
onClosed.invoke()
}
}
fun setOnEventActionListener(
onFullscreen: () -> Unit,
onClosed: () -> Unit
) {
this.onFullscreen = onFullscreen
this.onClosed = onClosed
}
private fun startHideAction() {
if (canHideActionButtons) {
hideActionHandler.postDelayed(hideActionRunnable, HIDE_ACTION_DURATION)
}
}
fun restartHideAction() {
hideActionHandler.removeCallbacks(hideActionRunnable)
if (canHideActionButtons) {
hideActionHandler.postDelayed(hideActionRunnable, HIDE_ACTION_DURATION)
}
}
fun hideActions() {
if (canHideActionButtons) {
imageCloseButton.invisible()
if (haveFullscreen) {
imageFullscreenButton.invisible()
}
}
}
fun showActions() {
imageCloseButton.visible()
if (haveFullscreen) {
imageFullscreenButton.visible()
}
}
}
In third class I can't understand what should be placed here to to change initial first Activity to another one
button.setOnClickListener{
println("pressed")
fun alert(context: Context, text: String) {
val intent = Intent(context, MainActivity2::class.java)
intent.putExtra("text", text)
context.startActivity(intent)
}
}
startService(intent) will try and start a new Service and unfortunately does not throw an error if you call it with an Activity class.
To launch MainActivity2 instead call context.startActivity(intent).
Related
I'm stuck on a problem with my app.
I'm trying to upload some photos to a cloud and then, I'll show to the user a list of url to copy, in order to use them in a second time.
I'm using RxWorker for this.
When I enqueue my workers list, everything seems fine (one OneTimeWorkRequestBuilder for each image), but then, when I retrieve the result, seems that my observe is triggered too many times.
In order to make this step suspendable, I've two lists (image and worker ones).
When a work is finished (both failure or success) I cancel both image and worker from their own list.
Here's my code. Thanks to everyone!
My Fragment:
class ImagesUploadFragment : Fragment() {
companion object {
private const val TAG = "ImagesUploadFragment"
private const val PHOTOS_LIST = "PHOTOS_LIST"
private const val WORK_NAME = "PHOTO_UPLOAD"
fun newInstance(optionalData: String?): ImagesUploadFragment {
val imagesUploadFragment = ImagesUploadFragment()
val bundle = Bundle()
bundle.putString(PHOTOS_LIST, optionalData)
imagesUploadFragment.arguments = bundle
return imagesUploadFragment
}
}
private lateinit var imagesUploadBinding: FragmentImagesUploadBinding
private var stepListener: StepListener? = null
private val mDownloadUrlsList = mutableListOf<String>()
private val mWorkManger: WorkManager by lazy { WorkManager.getInstance(requireContext()) }
private val mWorkRequestList = mutableListOf<OneTimeWorkRequest>()
private var isUploadPaused = false
private val mImagesList = mutableListOf<String>()
private val gson: Gson by inject()
override fun onAttach(context: Context) {
super.onAttach(context)
stepListener = context as? StepListener
}
private val imagesUploadViewModel: ImagesUploadViewModel by viewModel()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
imagesUploadBinding = FragmentImagesUploadBinding.inflate(inflater, container, false)
return imagesUploadBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val imagesList = arguments?.getString(PHOTOS_LIST)
imagesList?.let {
startImagesUpload(it)
}
imagesUploadBinding.successfullyUpdated.text = String.format(getString(R.string.succeeded_updates), 0)
imagesUploadBinding.workActionIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_pause))
imagesUploadBinding.buttonContinue.setOnClickListener {
stepListener?.onStepChange(4, gson.toJson(mDownloadUrlsList))
}
mWorkManger.cancelAllWorkByTag(TAG)
mWorkManger.cancelAllWork()
mWorkManger.pruneWork()
imagesUploadBinding.workActionIv.setOnClickListener {
if (!isUploadPaused) {
isUploadPaused = true
pauseUpload()
imagesUploadBinding.workActionIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_play))
} else {
isUploadPaused = false
resumeUpload()
imagesUploadBinding.workActionIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_pause))
}
}
setupWorkManagerObserve()
with(imagesUploadViewModel) {
observe(imagesListResource,
success = { imagesList ->
imagesUploadBinding.uploadProgress.max = imagesList.first.size
mImagesList.addAll(imagesList.first.map { it.jsonValue })
setupWorkerList()
},
failure = {
Timber.e(it)
}
)
observe(uploadedFileResource,
success = {
if(it.isNotEmpty()) mDownloadUrlsList.add(it)
imagesUploadBinding.buttonContinue.isVisible = mImagesList.size == 0
imagesUploadBinding.workActionIv.isVisible = mImagesList.size > 0
imagesUploadBinding.horizontalSpace.isVisible = mImagesList.size > 0
},
failure = {
Timber.e(it)
}
)
}
}
private fun pauseUpload() {
mWorkManger.cancelAllWork()
}
private fun resumeUpload() {
setupWorkerList()
}
private fun startImagesUpload(imagesListJson: String) {
imagesUploadViewModel.getImagesFromUri(imagesListJson)
}
private fun setupWorkManagerObserve() {
mWorkManger
.getWorkInfosByTagLiveData(TAG)
.observe(viewLifecycleOwner) {
var successNumber = 0
var failureNumber = 0
it.forEach { work ->
when {
work.state == WorkInfo.State.SUCCEEDED && work.state.isFinished -> {
successNumber++
val image = work.outputData.getString(PHOTO)
val uploadedFileJson = work.outputData.getString(UPLOAD_RESPONSE)
mWorkRequestList.removeAll { it.id == work.id }
mImagesList.removeAll { it == image }
uploadedFileJson?.let {
imagesUploadViewModel.getDownloadUrlFromResponse(it)
}
imagesUploadBinding.uploadProgress.progress = successNumber + failureNumber
imagesUploadBinding.successfullyUpdated.text =
String.format(getString(R.string.succeeded_updates), successNumber)
}
work.state == WorkInfo.State.RUNNING -> {
}
work.state == WorkInfo.State.FAILED -> {
failureNumber++
val image = work.outputData.getString(PHOTO)
mWorkRequestList.removeAll { it.id == work.id }
mImagesList.removeAll { it == image }
imagesUploadBinding.uploadProgress.progress = successNumber + failureNumber
imagesUploadBinding.failuredUpdated.text =
String.format(getString(R.string.failed_updates), failureNumber)
imagesUploadViewModel.addFailure()
}
else -> {
}
}
}
}
}
private fun setupWorkerList() {
for (i in 0 until mImagesList.size) {
val work = OneTimeWorkRequestBuilder<ImagesUploadWorkManager>()
.addTag(TAG)
.setInputData(Data.Builder().putString(PHOTO, mImagesList[i])
.build())
.build()
mWorkRequestList.add(work)
}
mWorkManger.enqueue(mWorkRequestList)
}
}
My worker:
class ImagesUploadWorkManager(val context: Context, parameters: WorkerParameters) : RxWorker(context, parameters) {
companion object {
const val PHOTO = "PHOTO"
const val UPLOAD_RESPONSE = "UPLOAD_RESPONSE"
}
private val uploadFilesUseCase: UploadFilesUseCase by inject(UploadFilesUseCase::class.java)
private val gson: Gson by inject(Gson::class.java)
override fun createWork(): Single<Result> {
val imageStringUri = inputData.getString(PHOTO)
val imageUri = Uri.parse(imageStringUri)
return Single.fromObservable(
uploadFilesUseCase.buildObservable(UploadFilesUseCase.Params(imageUri))
.doOnError {
Timber.e(it)
}
.map {
Result.success(workDataOf(UPLOAD_RESPONSE to gson.toJson(it), PHOTO to imageStringUri))
}
.onErrorReturn {
Timber.e(it)
Result.failure(workDataOf(PHOTO to imageStringUri))
}
)
}
}
My UseCase:
class UploadFilesUseCase(
private val context: Context,
private val getServerUseCase: GetServerUseCase,
) : UseCase<UploadFilesUseCase.Params, GoFileDataEntity>() {
private val goFileApi: GoFileApi by inject(GoFileApi::class.java)
override fun buildObservable(param: Params): Observable<GoFileDataEntity> {
return Observable.just(param.imageUri)
.flatMap {
val file = createTempFile(it) ?: throw Exception()
Observable.just(file)
}.flatMap { imageFile ->
getServerUseCase.buildObservable(GetServerUseCase.Params())
.flatMap { server ->
goFileApi.uploadFile(
String.format(BuildConfig.api_gofile_upload, server.server),
MultipartBody.Part.createFormData("file", imageFile.name, createUploadRequestBody(imageFile, "image/jpeg"))
).toObservable()
}.map { it.goFileDataDTO.toEntity() }
}
}
private fun createUploadRequestBody(file: File, mimeType: String) =
file.asRequestBody(mimeType.toMediaType())
private fun createTempFile(uri: Uri): File? {
val tempFile = File.createTempFile(System.currentTimeMillis().toString().take(4), ".jpg", context.externalCacheDir)
val inputStream = context.contentResolver.openInputStream(uri) ?: return null
FileOutputStream(tempFile, false).use { outputStream ->
var read: Int
val bytes = ByteArray(DEFAULT_BUFFER_SIZE)
while (inputStream.read(bytes).also { read = it } != -1) {
outputStream.write(bytes, 0, read)
}
outputStream.close()
}
inputStream.close()
return tempFile
}
data class Params(val imageUri: Uri)
}
I had a similar problem, and it looks like the queue was full from previous Workmanager enqueuings.
I did following during application start and now it works fine:
WorkManager.getInstance(mContext).cancelAllWork();
I am using exoplayer for playing videos .And for this we are used Fragment instance with pagerstateadapter and viewpager2.
But when scroll fast previous played video sound listen in background as well as in screen video means mix the sound in same video player.
Please help me how to solve this.
1.State adapter
class StoriesPagerAdapter(
fragment: Fragment,
val onClick1: VideoItemAdapter.OnItemClicked?,
val onlikeClick: VideoItemAdapter.OnLikeCLicked?,
val onFollowClick: VideoItemAdapter.OnFollowCLicked?,
val ontrendingClick: VideoItemAdapter.OnTrendCLicked?,
val oniconCLick: VideoItemAdapter.OnIconClick?) : FragmentStateAdapter(fragment) {
val dataList:MutableList<Gettrendingvideos.Data.Postlist>=mutableListOf()
override fun getItemCount(): Int {
return dataList.size
}
fun addAll(movies: MutableList<Gettrendingvideos.Data.Postlist>) {
for (movie in movies) {
add(movie)
}
}
fun add(moive: Gettrendingvideos.Data.Postlist) {
dataList.add(moive)
notifyItemInserted(dataList.size - 1)
}
override fun createFragment(position: Int): Fragment {
return StoryViewFragment.newInstance(
onClick1,
onlikeClick,
onFollowClick,
ontrendingClick,
oniconCLick,
dataList[position]
)
}}
2 Fragment
class StoryViewFragment : Fragment(), CommentFragment.onCommentCountIncrease {
private var storyUrl: String? = null
private var storiesDataModel: Gettrendingvideos.Data.Postlist? = null
lateinit var mView: View
private var simplePlayer: SimpleExoPlayer? = null
private var cacheDataSourceFactory: CacheDataSourceFactory? = null
private val simpleCache = MainApplication.simpleCache
private var toPlayVideoPosition: Int = -1
lateinit var viewModel: MainViewModel
lateinit var preferences: SecurePreferences
private var bool: Boolean? = false
var onItemClick: VideoItemAdapter.OnItemClicked? = null
var onlikeCLicked: VideoItemAdapter.OnLikeCLicked? = null
var onFollowCLicked: VideoItemAdapter.OnFollowCLicked? = null
var onTrendCLicked: VideoItemAdapter.OnTrendCLicked? = null
var onIconClick: VideoItemAdapter.OnIconClick? = null
lateinit var huserId: String
lateinit var token: String
companion object {
fun newInstance(
itemClicked: VideoItemAdapter.OnItemClicked?,
likeCLicked: VideoItemAdapter.OnLikeCLicked?,
onFollowCLicked: VideoItemAdapter.OnFollowCLicked?,
onTrendCLicked: VideoItemAdapter.OnTrendCLicked?,
onIconClick: VideoItemAdapter.OnIconClick?,
storiesDataModel: Gettrendingvideos.Data.Postlist
) = StoryViewFragment()
.apply {
arguments = Bundle().apply {
putParcelable(Constants.KEY_STORY_DATA, storiesDataModel)
}
this.onItemClick = itemClicked
this.onlikeCLicked = likeCLicked
this.onFollowCLicked = onFollowCLicked
this.onTrendCLicked = onTrendCLicked
this.onIconClick = onIconClick
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
mView = inflater.inflate(
R.layout.layout_main,
container,
false
)
return mView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, ViewModelFactory(RetrofitBuilder.apiService))
.get(MainViewModel::class.java)
preferences =
SecurePreferences(
requireActivity(),
AppConstants.preferenceName,
AppConstants.USER_DETAILS,
true
)
storiesDataModel = arguments?.getParcelable(Constants.KEY_STORY_DATA)
setData()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (!preferences.getString(AppConstants.USER_ID).equals(null)) {
huserId = preferences.getString(AppConstants.USER_ID)!!
Log.d("TAG", "onActivityCreated: $huserId")
}
}
#SuppressLint("SetTextI18n")
private fun setData() {
Log.d("TAG", "setData: $storiesDataModel")
mView.textview2.text = storiesDataModel?.user_name
mView.like_count.text = storiesDataModel?.total_likes.toString()
comment_count.text = storiesDataModel?.total_comments.toString()
mView.textview.text = storiesDataModel?.type
Glide.with(this).load(storiesDataModel?.user_profile_pic).placeholder(R.drawable.profile).into(mView.image)
if (storiesDataModel?.is_like == 0) {
mView.imageView4.setImageResource(R.drawable.ic_like)
} else {
mView.imageView4.setImageResource(R.drawable.ic_like_red)
}
if (storiesDataModel?.is_following!! == 0) {
mView.textview3.text = "Follow"
} else {
mView.textview3.text = "Following"
}
if (storiesDataModel?.user_id.toString()==preferences.getString(AppConstants.USER_ID)) {
mView.textview3.visibility = View.GONE
}
image.setOnClickListener {
if (preferences.getString(AppConstants.token).equals(null)) {
MainActivity().show(childFragmentManager, "")
} else {
preferences.put(
AppConstants.OtherProfile_UserId,
storiesDataModel?.user_id.toString()
)
}
}
val simplePlayer = getPlayer()
player_view_story.player = simplePlayer player_view_story.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL)
simplePlayer?.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
storyUrl = storiesDataModel?.video_url
prepareMedia(storiesDataModel)
}
override fun onPause() {
pauseVideo()
super.onPause()
}
override fun onResume() {
restartVideo()
super.onResume()
}
override fun onDestroy() {
releasePlayer()
super.onDestroy()
}
private fun pausePlayer() {
simplePlayer?.setPlayWhenReady(false)
simplePlayer?.getPlaybackState()
}
private fun startPlayer() {
simplePlayer?.setPlayWhenReady(true)
simplePlayer?.getPlaybackState()
}
private val playerCallback: Player.EventListener? = object : Player.EventListener {
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
}
override fun onPlayerError(error: com.google.android.exoplayer2.ExoPlaybackException?) {
super.onPlayerError(error)
}
}
private fun prepareVideoPlayer() {
simplePlayer = ExoPlayerFactory.newSimpleInstance(context)
cacheDataSourceFactory = CacheDataSourceFactory(
simpleCache,
DefaultHttpDataSourceFactory(
Util.getUserAgent(
context,
"exo"
)
)
)
}
private fun getPlayer(): SimpleExoPlayer? {
if (simplePlayer == null) {
prepareVideoPlayer()
}
return simplePlayer
}
private fun prepareMedia(datamodel: Gettrendingvideos.Data.Postlist?{
val uri = Uri.parse(datamodel?.video_url)
simplePlayer.repeatMode = Player.REPEAT_MODE_ONE
simplePlayer.playWhenReady = true
if (storiesDataModel!!.type == "following") {
following_page.typeface = Typeface.DEFAULT_BOLD
trending_page.setTypeface(null, Typeface.NORMAL)
} else {
following_page.setTypeface(null, Typeface.BOLD)
}
if (storiesDataModel.type == "treading") {
trending_page.typeface = Typeface.DEFAULT_BOLD
following_page.setTypeface(null, Typeface.NORMAL)
} else {
trending_page.setTypeface(null, Typeface.BOLD)
}
if (simplePlayer.playWhenReady == true) {
}
simplePlayer.addListener(playerCallback)
toPlayVideoPosition = -1
}
private fun setArtwork(drawable: Drawable, playerView: PlayerView) {
playerView.useArtwork = true
playerView.defaultArtwork = drawable
}
private fun playVideo() {
simplePlayer.playWhenReady = true
}
private fun restartVideo() {
if (simplePlayer == null) {
prepareMedia(storiesDataModel)
} else {
simplePlayer.seekToDefaultPosition()
simplePlayer.playWhenReady = true
}
}
private fun pauseVideo() {
simplePlayer.playWhenReady = false
}
private fun releasePlayer() {
simplePlayer.stop(true)
simplePlayer.release()
}}
override fun setMenuVisibility(menuVisible: Boolean) {
if (!menuVisible){
simplePlayer?.playWhenReady = false
simplePlayer?.pause()
}
super.setMenuVisibility(menuVisible)
}
JUST ADD THIS IN YOUR StoryViewFragment.
I have two screens first one has recycler view list of data and searchView above it that's filter data in this recycler, the view Model code of the first fragment
class MscInspectionViewModel(val activity: LaunchActivity, val mRootView: MscInspectFragment) :
BaseViewModel(),
SwipeRefreshLayout.OnRefreshListener {
val toolBarTitle: MutableLiveData<String> = MutableLiveData()
private val getDataError = MutableLiveData<Boolean>()
var listType = MutableLiveData<Int>()
val hint = MutableLiveData<String>()
private var isRefreshing: Boolean = false
private var mSharedPreferences: SharedPreferences? = null
val dataListAdapter = ContainersUnderCheckAdapter(activity)
val backClickListener = View.OnClickListener { activity.supportFragmentManager.popBackStack() }
val filterDataByTab = object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
when (tab!!.text) {
activity.resources.getString(R.string.cidPending) -> {
listType.value = 0
getPendingData()
}
activity.resources.getString(R.string.cidDone) -> {
listType.value = 1
getDoneData()
}
}
}
}
val filterData = object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
if (query.length > 2) {
val mQuery = Utility(activity).switchArabicNumerals(query)
dataListAdapter.getFilter(3, listType.value!!).filter(mQuery)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
return true
}
override fun onQueryTextChange(newText: String): Boolean {
if (newText.length > 2) {
val mQuery = Utility(activity).switchArabicNumerals(newText)
dataListAdapter.getFilter(3, listType.value!!).filter(mQuery)
}
return false;
}
}
val closeImgListener = View.OnClickListener {
mRootView.svSearchMSC.setQuery("", true)
if (listType.value == 1) {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else if (listType.value == 0) {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
}
}
init {
listType.value = 0
mSharedPreferences = getDefaultSharedPreferences(activity.applicationContext)
toolBarTitle.value = activity.resources.getString(R.string.mscInspectTitle)
hint.value = activity.resources.getString(R.string.msc_search)
getData()
}
fun getData() {
onRetrievePostListStart()
subscription = apiAccount.getContainersUnderCheck(
"getContainersUnderCheck",
mSharedPreferences!!.getString(Constants.CFID, "")!!,
mSharedPreferences!!.getString(Constants.CFTOKEN, "")!!
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {}
.doOnTerminate {}
.subscribe({ result ->
result?.let {
if (result.ResponseCode != null && result.ResponseCode.trim() != "000") {
onRetrievePostListError(result.ResponseMessage)
} else {
result.ContainersData?.let { it1 -> onRetrievePostListSuccess(it1) }
}
}
}, { throwable ->
android.util.Log.e("getDataInquiry", throwable.message!!)
onRetrievePostListError(activity.resources.getString(R.string.general_error))
})
}
private fun getPendingData() {
val query = mRootView.svSearchMSC.query.toString()
if (query == "") {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
} else {
if (query.length > 2) {
dataListAdapter.getFilter(3, listType.value!!).filter(query)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
}
}
private fun getDoneData() {
val query = mRootView.svSearchMSC.query.toString()
if (query == "") {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else {
if (query.length > 2) {
dataListAdapter.getFilter(3, listType.value!!).filter(query)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
}
}
private fun onRetrievePostListStart() {
loading.value = true
}
private fun onRetrievePostListFinish() {
loading.value = false
isRefreshing = false
}
private fun onRetrievePostListSuccess(containersData: List<ContainersData>) {
onRetrievePostListFinish()
dataListAdapter.updateInquiryAdapter(containersData as ArrayList<ContainersData>)
if (listType.value == 1) {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else if (listType.value == 0) {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
}
}
private fun onRetrievePostListError(message: String?) {
onRetrievePostListFinish()
getDataError.value = true
errorMessage.value = message
}
override fun onCleared() {
super.onCleared()
subscription.dispose()
}
override fun onRefresh() {
isRefreshing = true
getData()
}
}
adapter is :
class ContainersUnderCheckAdapter(val activity: LaunchActivity) :
RecyclerView.Adapter<ContainersUnderCheckAdapter.ViewHolder>() {
private lateinit var mDataSet: ArrayList<ContainersData>
private lateinit var mDataSetFiltered: ArrayList<ContainersData>
fun updateInquiryAdapter(dataSet: ArrayList<ContainersData>) {
mDataSet = ArrayList()
mDataSet.clear()
mDataSet.addAll(dataSet)
mDataSetFiltered = mDataSet
getFilter(2, 1).filter("PENDING")
// notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ContianerItemFieldLayoutBinding = DataBindingUtil
.inflate(
LayoutInflater.from(parent.context),
R.layout.contianer_item_field_layout,
parent,
false
)
return ViewHolder(binding, activity)
}
override fun getItemCount(): Int {
return if (::mDataSetFiltered.isInitialized) mDataSetFiltered.size else 0
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(mDataSetFiltered[position])
}
operator fun get(position: Int): ContainersData {
return mDataSetFiltered.get(position)
}
/**
* #filterType :
* IF 1 : filter on Data Type RJCTD + APPROVED
* 2 : filter on Data Type PENDING
* 3 :
*/
fun getFilter(filterType: Int, listType: Int): Filter {
return object : Filter() {
override fun performFiltering(charSequence: CharSequence): FilterResults {
val charString = charSequence.toString()
mDataSetFiltered = if (charString.isEmpty()) {
mDataSet
} else {
val filteredList = ArrayList<ContainersData>()
for (row in mDataSet) {
when (filterType) {
1 -> {
if (row.status == "RJCTD" || row.status == "APPROVED") {
filteredList.add(row)
}
}
2 -> {
if (row.status == charString) {
filteredList.add(row)
}
}
3 -> {
when (listType) {
0 -> {
if ((row.CID!!.contains(charString.toUpperCase(Locale.ROOT)) || row.TN!!.contains(
charSequence
) || row.PN!!.contains(charSequence)) && row.status == "PENDING"
) {
filteredList.add(row)
}
}
1 -> {
if ((row.CID!!.contains(charString.toUpperCase(Locale.ROOT)) || row.TN!!.contains(
charSequence
) || row.PN!!.contains(charSequence)) && row.status != "PENDING"
) {
filteredList.add(row)
}
}
}
}
}
}
filteredList
}
val filterResults = FilterResults()
filterResults.values = mDataSetFiltered
return filterResults
}
override fun publishResults(
charSequence: CharSequence,
filterResults: FilterResults
) {
if (::mDataSetFiltered.isInitialized) {
mDataSetFiltered = try {
filterResults.values as ArrayList<ContainersData>
} catch (e: Exception) {
Log.e("mDataSetFiltered",e.message!!)
ArrayList()
}
when (filterType) {
1->{
mDataSetFiltered.sortWith(Comparator { p0, p1 -> p1!!.UpdateDate.compareTo(p0!!.UpdateDate) })
}
2->{
mDataSetFiltered.sortWith(Comparator { p0, p1 -> p0!!.ID!!.compareTo(p1.ID!!) })
}
}
}
// refresh the list with filtered data
notifyDataSetChanged()
}
}
}
class ViewHolder(
private val binding: ContianerItemFieldLayoutBinding,
val activity: LaunchActivity
) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = MscInspectionListViewModel(activity)
fun bind(data: ContainersData) {
viewModel.bind(data)
binding.viewModel = viewModel
}
}
}
any data in this recycler on click go to fragment has tow recycler first one to show data, the second one to pick Images
the second-page code
class MSCDataFragment : Fragment() {
lateinit var rootView: View
lateinit var activity: LaunchActivity
lateinit var utility: Utility
lateinit var loadingView: LoadingView
private lateinit var viewModel: MSCDataViewModel
private lateinit var binding: FragmentMscdataBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (getActivity() != null) {
activity = getActivity() as LaunchActivity
utility = Utility(activity)
loadingView = LoadingView(activity)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_mscdata, container, false)
rootView = binding.root
initial()
return rootView
}
private fun initial() {
viewModel = ViewModelProvider(
this, ViewModelFactory(
activity,
arguments!!.getSerializable("Data") as ContainersData
)
).get(MSCDataViewModel::class.java)
binding.viewModel = viewModel
// binding.imgList.layoutManager = GridLayoutManager(activity, 3)
binding.containerInfo.layoutManager = LinearLayoutManager(activity)
binding.openCIDNotValid.typeface =
Typeface.createFromAsset(activity.assets, "Bahij_Janna-Regular.ttf")
binding.openCIDNotValid.setOnCheckedChangeListener(viewModel.onOpenCidNotValidListener)
viewModel.loading.observe(this, Observer { loading ->
loading?.let {
if (it) {
loadingView.show()
} else {
loadingView.dismiss()
}
}
})
viewModel.errorMessage.observe(this, Observer { msg ->
msg?.let {
utility.ShowToast(msg)
}
})
viewModel.imagesAdapters2.observe(this, Observer { msg ->
msg?.let {
binding.imgList.apply {
layoutManager = GridLayoutManager(activity, 3)
adapter = it
}
}
})
rootView.toolbar_Back.setOnClickListener(viewModel.backClickListener)
binding.btnAddImages.setOnClickListener(viewModel.pickImages)
binding.successContianer.setOnClickListener(viewModel.correctContainer)
binding.damagedContianer.setOnClickListener(viewModel.wrongContainer)
}
}
the view model is :
class MSCDataViewModel(val activity: LaunchActivity, val containersData: ContainersData) :
BaseViewModel(), GetImagesListener {
#Inject
lateinit var restApiAccount: RestApiAccount
val toolBarTitle: MutableLiveData<String> = MutableLiveData()
val ButtonText: MutableLiveData<String> = MutableLiveData()
var openCIDNotValidVisibility = MutableLiveData<Int>()
private val getDataError = MutableLiveData<Boolean>()
val btnImagesVisibility = MutableLiveData<Int>()
var imgeNoteVisibility = MutableLiveData<Int>()
var successVisibility = MutableLiveData<Int>()
var damagedVisibility = MutableLiveData<Int>()
var notesVisibility = MutableLiveData<Int>()
val btnVisibility = MutableLiveData<Int>()
var canNotOpen = MutableLiveData<Int>()
private val images = ArrayList<Image>()
var utility = Utility(activity)
private var CURRENTINDEX = 0
private var mSharedPreferences: SharedPreferences? = null
val DataListAdapter = ContainerDataAdapter(activity)
var imagesAdapter = ContainerImagesAdapter(activity, containersData.status!!, ArrayList())
val imagesAdapters2 = MutableLiveData<ContainerImagesAdapter2>()
val userInfo: UserInfo
val backClickListener = View.OnClickListener { activity.supportFragmentManager.popBackStack() }
val pickImages = View.OnClickListener {
pickImages()
}
val correctContainer = View.OnClickListener {}
val onOpenCidNotValidListener =
CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
successVisibility.value = View.GONE
canNotOpen.value = 1
} else {
canNotOpen.value = 0
successVisibility.value = View.VISIBLE
}
}
val wrongContainer = View.OnClickListener {}
var mscNotes: ObservableField<String> = ObservableField("")
init {
canNotOpen.value = 0
mSharedPreferences =
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
toolBarTitle.value = containersData.CID
ButtonText.value = activity.resources.getString(R.string.cleanContianer)
userInfo = utility.readObjectFromSharedPreferences(
mSharedPreferences,
Constants.USER_INFO_KEY,
UserInfo::class.java
) as UserInfo
openCIDNotValidVisibility.value = View.GONE
fillData()
}
private fun fillData() {
val data: LinkedHashMap<String, String> = containersData.data!!
val captionsMap = utility.readObjectFromSharedPreferences(
mSharedPreferences, Constants.CAPTIONS_MAP_KEY,
HashMap::class.java
) as HashMap<String, String>
if (containersData.data.size > 0) {
val list = ArrayList<KeyValueModel>()
for (inside in data.keys) {
val ky = captionsMap[inside]
val value = data[inside].toString()
ky?.let { KeyValueModel(it, value) }?.let { list.add(it) }
}
DataListAdapter.updateInquiryAdapter(list)
} else {
errorMessage.value = activity.resources.getString(R.string.no_data)
}
if (containersData.ImageList != null && containersData.ImageList.isNotEmpty()) {
imagesAdapter.updateContainerImagesAdapter(containersData.ImageList)
}
}
private fun pickImages() {
activity.setCallBack(this)
val pictureDialog: AlertDialog
val builder = activity.let { AlertDialog.Builder(it) }
val dialogView = View.inflate(activity, R.layout.choose_camera_method, null)
builder.setView(dialogView)
val nafithPopupContainer = dialogView.findViewById<RelativeLayout>(R.id.RLTitle)
nafithPopupContainer.setBackgroundColor(
ContextCompat.getColor(
activity,
R.color.mainColor
)
)
val popUpGallery = dialogView.findViewById<LinearLayout>(R.id.PopupGellary)
val popUpCamera = dialogView.findViewById<LinearLayout>(R.id.PopupCamera)
pictureDialog = builder.create()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Objects.requireNonNull<Window>(pictureDialog.window)
.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
} else {
if (pictureDialog.window != null) {
pictureDialog.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
}
popUpGallery.setOnClickListener {
fromGallery()
pictureDialog.dismiss()
}
popUpCamera.setOnClickListener {
fromCamera()
pictureDialog.dismiss()
}
val popupClose = dialogView.findViewById<ImageView>(R.id.popupClose)
popupClose.setOnClickListener { pictureDialog.dismiss() }
pictureDialog.show()
}
private fun fromGallery() {
ImagePicker.create(activity)
.toolbarImageTitle(activity.resources.getString(R.string.get_image))
.toolbarArrowColor(ContextCompat.getColor(activity, R.color.colorWhite))
.showCamera(false)
.limit(6)
.start()
}
private fun fromCamera() {
ImagePicker.cameraOnly().start(activity)
}
override fun onGetImage(image: Image) {
imgeNoteVisibility.value = View.GONE
imagesAdapter.updateContainerImagesAdapter(image)
images.add(image)
}
override fun addingImagesDone(mImages: MutableList<Image>) {
images.clear()
images.addAll(mImages)
imgeNoteVisibility.value = View.GONE
val listString :ArrayList<String> = ArrayList()
for (i in mImages.indices){
listString.add(mImages[i].path)
}
imagesAdapters2.value = ContainerImagesAdapter2(activity,containersData.status!!,listString)
imagesAdapters2.value!!.notifyItemRangeChanged(0,listString.size)
}
override fun onImgDelete(image: String) {
var x = 0
try {
for (i in 0 until images.size) {
x = i
if (images[i].path == image) {
images.remove(images[i])
}
}
} catch (e: Exception) {
Log.e("errorImages", e.message!!)
Log.e("xx", x.toString())
}
}
private fun onRetrievePostListStart() {
loading.value = true
}
private fun onRetrievePostListFinish() {
loading.value = false
}
private fun onRetrievePostListSuccess(msg: String?) {
onRetrievePostListFinish()
}
private fun onRetrievePostListError(message: String?) {
onRetrievePostListFinish()
getDataError.value = true
errorMessage.value = message
}
}
Adapter code is :
class ContainerImagesAdapter2() : RecyclerView.Adapter<ContainerImagesAdapter2.ViewHolder>() {
var status: String = ""
lateinit var activity: LaunchActivity
lateinit var utility: Utility
constructor(
mActivity: LaunchActivity,
mStatus: String,
pathsList: ArrayList<String>
) : this() {
activity = mActivity
pathsDataSet = pathsList
status = mStatus
utility = Utility(activity)
}
private var pathsDataSet: ArrayList<String> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ContianerImageFieldBinding = DataBindingUtil
.inflate(
LayoutInflater.from(parent.context),
R.layout.contianer_image_field,
parent,
false
)
return ViewHolder(binding, activity)
}
override fun getItemCount(): Int {
return pathsDataSet.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindPath(pathsDataSet[position], position)
}
inner class ViewHolder(
private val binding: ContianerImageFieldBinding,
val activity: LaunchActivity
) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = MscImagesListViewModel(activity)
fun bindPath(data: String, position: Int) {
viewModel.bindPath(data)
binding.viewModel = viewModel
if (status != "PENDING") {
binding.closeImg.visibility = View.GONE
}
binding.closeImg.setOnClickListener {}
binding.mainImg.setOnClickListener {
val fragment = FullImageFragment()
val bundle = Bundle()
val list = ArrayList<String>()
for (item in 0 until pathsDataSet.size) {
list.add(pathsDataSet[item])
}
bundle.putSerializable("ImageList", list)
bundle.putInt("Position", position)
fragment.arguments = bundle
activity.supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment).addToBackStack(fragment.tag)
.commit()
}
}
}
}
if you filter data using search view in the first-page and pick images in the second page , list of picked images doesn't appear, if you going to the second page without filtering data everything ok
solve Problem found
Just Update constraint-layout library in gradle dependencies to version '2.0.0-beta4'
I'm Joss this is my first question in stackoverflow
I want to activate(isEnabled = true) the button when a geofence event occurs.
The code that works sendNotification is woriking but I want to add function add button(btn_done) activate
GeofenceTransitionService.kt
class GeofenceTransitionService : IntentService("GeoTrIntentService") {
companion object {
private const val LOG_TAG = "GeoTrIntentService"
}
override fun onHandleIntent(intent: Intent?) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
val errorMessage = GeofenceErrorMessages.getErrorString(this,
geofencingEvent.errorCode)
Log.e(LOG_TAG, errorMessage)
return
}
handleEvent(geofencingEvent)
}
private fun handleEvent(event: GeofencingEvent) {
if (event.geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
btn_done.isEnabled = true //How should I code this?
val reminder = getFirstReminder(event.triggeringGeofences)
val message = reminder?.message
val latLng = reminder?.latLng
if (message != null && latLng != null) {
sendNotification(this, message, latLng)
}
}
}
private fun getFirstReminder(triggeringGeofences: List<Geofence>): Reminder? {
val firstGeofence = triggeringGeofences[0]
return (application as
ReminderApp).getRepository().get(firstGeofence.requestId)
}
}
ReminderRepository.kt
class ReminderRepository(private val context: Context) {
companion object {
private const val PREFS_NAME = "ReminderRepository"
private const val REMINDERS = "REMINDERS"
}
private val preferences = context.getSharedPreferences(PREFS_NAME,
Context.MODE_PRIVATE)
private val gson = Gson()
private val geofencingClient = LocationServices.getGeofencingClient(context)
private val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(context, GeofenceTransitionService::class.java)
PendingIntent.getService(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT)
}
fun add(reminder: Reminder,
success: () -> Unit,
failure: (error: String) -> Unit) {
// 1
val geofence = buildGeofence(reminder)
if (geofence != null
&& ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED) {
// 2
geofencingClient
.addGeofences(buildGeofencingRequest(geofence),
geofencePendingIntent)
.addOnSuccessListener {
// 3
saveAll(getAll() + reminder)
success()
}
.addOnFailureListener {
// 4
failure(GeofenceErrorMessages.getErrorString(context,
it))
}
}
}
private fun buildGeofence(reminder: Reminder): Geofence? {
val latitude = reminder.latLng?.latitude
val longitude = reminder.latLng?.longitude
val radius = reminder.radius
if (latitude != null && longitude != null && radius != null) {
return Geofence.Builder()
.setRequestId(reminder.id)
.setCircularRegion(
latitude,
longitude,
radius.toFloat()
)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build()
}
return null
}
private fun buildGeofencingRequest(geofence: Geofence): GeofencingRequest {
return GeofencingRequest.Builder()
.setInitialTrigger(0)
.addGeofences(listOf(geofence))
.build()
}
fun remove(reminder: Reminder,
success: () -> Unit,
failure: (error: String) -> Unit) {
geofencingClient
.removeGeofences(listOf(reminder.id))
.addOnSuccessListener {
saveAll(getAll() - reminder)
success()
}
.addOnFailureListener {
failure(GeofenceErrorMessages.getErrorString(context, it))
}
}
private fun saveAll(list: List<Reminder>) {
preferences
.edit()
.putString(REMINDERS, gson.toJson(list))
.apply()
}
fun getAll(): List<Reminder> {
if (preferences.contains(REMINDERS)) {
val remindersString = preferences.getString(REMINDERS, null)
val arrayOfReminders = gson.fromJson(remindersString,
Array<Reminder>::class.java)
if (arrayOfReminders != null) {
return arrayOfReminders.toList()
}
}
return listOf()
}
fun get(requestId: String?) = getAll().firstOrNull { it.id == requestId }
fun getLast() = getAll().lastOrNull()
}
MainActivity.kt (show only important code)
class MainActivity : BaseActivity(), OnMapReadyCallback,
GoogleMap.OnMarkerClickListener {
companion object {
private const val MY_LOCATION_REQUEST_CODE = 329
private const val NEW_REMINDER_REQUEST_CODE = 330
private const val EXTRA_LAT_LNG = "EXTRA_LAT_LNG"
private const val LOG_TAG = "GeoTrIntentService"
fun newIntent(context: Context, latLng: LatLng): Intent {
val intent = Intent(context, MainActivity::class.java)
intent.putExtra(EXTRA_LAT_LNG, latLng)
return intent
}
}
private var map: GoogleMap? = null
private lateinit var locationManager: LocationManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
newReminder.visibility = View.GONE
currentLocation.visibility = View.GONE
newReminder.setOnClickListener {
map?.run {
val intent = NewReminderActivity.newIntent(
this#MainActivity,
cameraPosition.target,
cameraPosition.zoom)
startActivityForResult(intent, NEW_REMINDER_REQUEST_CODE)
val geofencingEvent = GeofencingEvent.fromIntent(intent)
qwer(geofencingEvent)
}
}
btn_done.setOnClickListener {
val intent = Intent(this, sub::class.java)
startActivity(intent)
}
locationManager = getSystemService(Context.LOCATION_SERVICE) as
LocationManager
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
MY_LOCATION_REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data:
Intent?) {
if (requestCode == NEW_REMINDER_REQUEST_CODE && resultCode ==
Activity.RESULT_OK) {
showReminders()
val reminder = getRepository().getLast()
map?.moveCamera(CameraUpdateFactory.newLatLngZoom(reminder?.latLng,
15f))
Snackbar.make(main, R.string.reminder_added_success,
Snackbar.LENGTH_LONG).show()
}
}
activate_maps.xml
<Button
android:id="#+id/btn_done"
android:enabled="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Arrive" />
You can use kotlin interface to change button state in app.
Create Interface for button state change in GeofenceTransitionService.kt
class GeofenceTransitionService : IntentService("GeoTrIntentService") {
interface ChangeViewState {
fun changeButtonState() : Button
}
companion object {
private const val LOG_TAG = "GeoTrIntentService"
}
override fun onHandleIntent(intent: Intent?) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
val errorMessage = GeofenceErrorMessages.getErrorString(this,
geofencingEvent.errorCode)
Log.e(LOG_TAG, errorMessage)
return
}
handleEvent(geofencingEvent)
}
private val changeViewState : ChangeViewState? = null
private fun handleEvent(event: GeofencingEvent) {
if (event.geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
//btn_done.isEnabled = true //How should I code this?
changeViewState?.changeButtonState().isEnabled = true
val reminder = getFirstReminder(event.triggeringGeofences)
val message = reminder?.message
val latLng = reminder?.latLng
if (message != null && latLng != null) {
sendNotification(this, message, latLng)
}
}
}
private fun getFirstReminder(triggeringGeofences: List<Geofence>): Reminder? {
val firstGeofence = triggeringGeofences[0]
return (application as
ReminderApp).getRepository().get(firstGeofence.requestId)
}
}
Implement interface in main activity
class MainActivity : BaseActivity(), OnMapReadyCallback,
GoogleMap.OnMarkerClickListener, GeofenceTransitionService.ChangeViewState {
companion object {
private const val MY_LOCATION_REQUEST_CODE = 329
private const val NEW_REMINDER_REQUEST_CODE = 330
private const val EXTRA_LAT_LNG = "EXTRA_LAT_LNG"
private const val LOG_TAG = "GeoTrIntentService"
fun newIntent(context: Context, latLng: LatLng): Intent {
val intent = Intent(context, MainActivity::class.java)
intent.putExtra(EXTRA_LAT_LNG, latLng)
return intent
}
}
private var map: GoogleMap? = null
private lateinit var locationManager: LocationManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
// Button Ref
newReminder.visibility = View.GONE
currentLocation.visibility = View.GONE
newReminder.setOnClickListener {
map?.run {
val intent = NewReminderActivity.newIntent(
this#MainActivity,
cameraPosition.target,
cameraPosition.zoom
)
startActivityForResult(intent, NEW_REMINDER_REQUEST_CODE)
val geofencingEvent = GeofencingEvent.fromIntent(intent)
qwer(geofencingEvent)
}
}
btn_done.setOnClickListener {
val intent = Intent(this, sub::class.java)
startActivity(intent)
}
locationManager = getSystemService(Context.LOCATION_SERVICE) as
LocationManager
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
!= PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
MY_LOCATION_REQUEST_CODE
)
}
}
override fun onActivityResult(
requestCode: Int, resultCode: Int, data:
Intent?
) {
if (requestCode == NEW_REMINDER_REQUEST_CODE && resultCode ==
Activity.RESULT_OK
) {
showReminders()
val reminder = getRepository().getLast()
map?.moveCamera(
CameraUpdateFactory.newLatLngZoom(
reminder?.latLng,
15f
)
)
Snackbar.make(
main, R.string.reminder_added_success,
Snackbar.LENGTH_LONG
).show()
}
}
// Implement interface
override fun changeButtonState(): Button {
val button = findViewById(R.id.btn_done) as Button
return button
}
}
Im newbie Android(Kotlin), I want send data from my app to widget and show on home screen. Thank you so much. Sorry for bad english.
In MainActivity class, I am using Room to save it.
private fun saveNewCityCollection(informationWeather: InformationWeather, state: Boolean = Constants.OTHER_LOCATION) {
val weatherRepository = WeatherRepository(applicationContext)
val cityCollection = CityCollection()
cityCollection.cityName = informationWeather.location.name
cityCollection.countryName = informationWeather.location.country
cityCollection.temp = informationWeather.current.temp
cityCollection.appTemp = informationWeather.current.appTemp
cityCollection.humidity = informationWeather.current.humidity
cityCollection.wind = informationWeather.current.windSpeed
cityCollection.cloud = informationWeather.current.cloud
cityCollection.description = informationWeather.current.condition.description
cityCollection.icon = informationWeather.current.condition.icon
cityCollection.date = informationWeather.current.date
cityCollection.day = informationWeather.current.isDay
if (location == cityCollection.cityName) {
cityCollection.state = true
}
if (state) {
cityCollection.state = true
val editor = getSharedPreferences(getString(R.string.shared_preference_name), Context.MODE_PRIVATE).edit()
editor.putString(Constants.NAME_LOCATION, cityCollection.cityName)
editor.apply()
}
weatherRepository.insert(cityCollection, this)
}
And my model: CityColection, I am using Parcelable.
class CityCollection() : Parcelable {
#PrimaryKey
#NonNull
#ColumnInfo(name = "cityname")
lateinit var cityName: String
lateinit var countryName: String
var state = Constants.OTHER_LOCATION
var temp: Float = 0F
var appTemp: Float = 0F
var humidity: Int = 0
var wind: Float = 0F
var cloud: Int = 0
var day: Int = 0
lateinit var description: String
var icon: String = "na"
var date: String = "dd/mm/yy"
constructor(parcel: Parcel) : this() {
cityName = parcel.readString()
countryName = parcel.readString()
state = parcel.readByte() != 0.toByte()
temp = parcel.readFloat()
appTemp = parcel.readFloat()
humidity = parcel.readInt()
wind = parcel.readFloat()
cloud = parcel.readInt()
day = parcel.readInt()
description = parcel.readString()
icon = parcel.readString()
date = parcel.readString()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(cityName)
parcel.writeString(countryName)
parcel.writeByte(if (state) 1 else 0)
parcel.writeFloat(temp)
parcel.writeFloat(appTemp)
parcel.writeInt(humidity)
parcel.writeFloat(wind)
parcel.writeInt(cloud)
parcel.writeInt(day)
parcel.writeString(description)
parcel.writeString(icon)
parcel.writeString(date)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<CityCollection> {
override fun createFromParcel(parcel: Parcel): CityCollection {
return CityCollection(parcel)
}
override fun newArray(size: Int): Array<CityCollection?> {
return arrayOfNulls(size)
}
}
}
In My Fragment, I am getParcelable to get data.
private fun initData() {
val bundle = arguments
if (bundle != null) {
val cityCollection: CityCollection = bundle.getParcelable(Constants.CITY_COLLECTION)
tvWind.text = cityCollection.wind.toInt().toString() + " km/h"
}
}
And my ViewPagerAdapter:
class ViewPagerAdapter(fm: FragmentManager, private var listCityCollection: MutableList<CityCollection>)
: FragmentStatePagerAdapter(fm) {
override fun getItem(position: Int): Fragment? {
return newFragment(listCityCollection[position])
}
override fun getItemPosition(`object`: Any): Int {
return PagerAdapter.POSITION_NONE
}
override fun getCount(): Int {
return listCityCollection.size
}
private fun newFragment(cityCollection: CityCollection): FragmentShowWeatherForecast {
val fragmentShowWeatherForecast = FragmentShowWeatherForecast()
val bundle = Bundle()
bundle.putParcelable(Constants.CITY_COLLECTION, cityCollection)
fragmentShowWeatherForecast.arguments = bundle
return fragmentShowWeatherForecast
}
}
My Widget class:
class WeatherWidget : AppWidgetProvider() {
override fun onEnabled(context: Context?) {
super.onEnabled(context)
val intent = Intent(context, WeatherWidget::class.java)
intent.action = Constants.UPDATE_WIDGET
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
val alarmManager = context?.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), (Constants.UPDATE_INTERVAL * 1000).toLong(), pendingIntent)
}
#SuppressLint("SimpleDateFormat")
override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) {
if (appWidgetIds != null) {
for (i in appWidgetIds.indices) {
val views = RemoteViews(context?.packageName, R.layout.widget_weather)
val openApp = Intent(context, MainActivity::class.java)
val pIntent = PendingIntent.getActivity(context, 0, openApp, 0)
views.setOnClickPendingIntent(R.id.rlWidget, pIntent)
appWidgetManager?.updateAppWidget(appWidgetIds[i], views)
}
}
}
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
super.onDeleted(context, appWidgetIds)
Toast.makeText(context, "onDeleted()", Toast.LENGTH_LONG).show()
}
override fun onDisabled(context: Context?) {
super.onDisabled(context)
val intent = Intent(context, WeatherWidget::class.java)
intent.action = Constants.UPDATE_WIDGET
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
val alarmManager = context?.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.cancel(pendingIntent)
}
}
My solution is save your model as json in SharedPreferences, some thing like below
preferenceApi.put(PREFS_WEATHER_DATA + appWidgetId,
new GsonBuilder().create().toJson(data, WeatherData.class));
and get it in onUpdate() of Provider of Widget
SharedPreferenceApi preferenceApi = new SharedPreferenceApi(context);
Gson gson = new Gson();
String jsonData = preferenceApi.get(PREFS_WEATHER_DATA + widgetId, String.class);
WeatherData weatherData = gson.fromJson(jsonData, WeatherData.class);
hope this helps