Skip to content

Commit

Permalink
Initial support for overlay draggable view (over other app)
Browse files Browse the repository at this point in the history
- Update usage readme
- Update example

Solve #5
  • Loading branch information
hyuwah committed May 4, 2020
1 parent 4439948 commit f97cbf7
Show file tree
Hide file tree
Showing 13 changed files with 347 additions and 26 deletions.
3 changes: 0 additions & 3 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 88 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ dependencies {

**Note:** check the number on Jitpack badge above for latest version

---

## Usage

### CustomView (XML)
### Draggable Inside App
#### CustomView (XML)

Currently i've only provide CustomView that extends ImageView. For other view, see **Programmatically** usage below

#### Customizable Attributes
##### Customizable Attributes

**DraggableImageView**

Expand All @@ -47,7 +50,7 @@ Attribute | Value (Default) | XML | Code
Animate | true, false (false) | animate | setAnimate(boolean isAnimate)
Sticky Axis | NON_STICKY, STICKY_AXIS_X, STICKY_AXIS_Y, STICKY_AXIS_XY (NON_STICKY) | sticky | setStickyAxis(int axis)

#### On Layout XML file
##### On Layout XML file
```xml
<io.github.hyuwah.draggableviewlib.DraggableImageView
android:src="@mipmap/ic_launcher_round"
Expand All @@ -56,7 +59,7 @@ Sticky Axis | NON_STICKY, STICKY_AXIS_X, STICKY_AXIS_Y, STICKY_AXIS_XY (NON_STIC
android:layout_height="wrap_content"/>
```

#### On Activity / Fragment file
##### On Activity / Fragment file
```kotlin
var dv = findViewById<DraggableImageView>(R.id.draggableView)
dv.setOnClickListener {
Expand All @@ -66,9 +69,9 @@ dv.setOnClickListener {

You can add a DraggableListener programmatically via `setListener()` method directly on the view, see below explanation about the listener

### Programmatically
#### Programmatically

#### Using extension (Kotlin)
##### Using extension (Kotlin)

You can extent any view or viewgroup to be draggable (i.e. Button, FrameLayout, Linearlayout, LottieView, etc)

Expand Down Expand Up @@ -114,7 +117,7 @@ tv.makeDraggable() // all default
// - DraggableListener implementation (default is null)
```

#### Using DraggableUtils (Java)
##### Using DraggableUtils (Java)

If you're on java class, you could do it with the help of DraggableUtils

Expand Down Expand Up @@ -167,7 +170,7 @@ DraggableUtils.makeDraggable(button) // all default
// - DraggableListener implementation (default is null)
```

#### DraggableListener
##### DraggableListener
There's an interface `DraggableListener` to listen to the `View` while being dragged / moved

```kotlin
Expand All @@ -189,6 +192,83 @@ someView.makeDraggable(object: DraggableListener{

Check example module [kotlin](https://github.com/hyuwah/DraggableView/blob/master/example/src/main/java/io/github/hyuwah/draggableview/MainActivity.kt), [java](https://github.com/hyuwah/DraggableView/blob/master/example/src/main/java/io/github/hyuwah/draggableview/JavaMainActivity.java) for actual implementation

### Draggable over other App (Overlay)

> Tested working on API 25, 28 & 29
> Not working as of now on API 19 (on investigation)
This is the simplest way to setup an overlay draggable view, assuming it will be started from an activity.

Some notes:
* On the activity, implement `OverlayDraggableListener`
* We need to make the view programmatically, here i'm creating a TextView, you can also inflate a layout
* You need to make the view as global variable
* Here i'm omitting the params / using default params for `makeOverlayDraggable()`

This will create an overlay draggable view that tied to the activity's lifecycle, from `onCreate` until `onDestroy`.
It is recommended to create the overlay draggable view on a `Service` instead of an activity, because of that reason.

```kotlin
class ExampleActivity: AppCompatActivity(), OverlayDraggableListener {

private lateinit var overlayView: TextView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_example)
overlayView = TextView(this)
overlayView.text = "Overlay Text View"
overlayView.textSize = 32f
overlayView.setShadowLayer(10f, 5f, 5f, Color.rgb(56, 56, 56))
overlayView.setOnClickListener {
Toast.makeText(this, "Overlay view clicked", Toast.LENGTH_SHORT).show()
}
var params = overlayView.makeOverlayDraggable(this)
windowManager.addView(overlayView, params) // Show overlay view
}

// From OverlayDraggableListener
override fun onParamsChanged(updatedParams: WindowManager.LayoutParams) {
windowManager.updateViewLayout(overlayView, updatedParams) // Move overlay view
}

override fun onDestroy() {
super.onDestroy()
windowManager.removeViewImmediate(overlayView) // Remove overlay view
}
}
```

You also need to add some permission

```xml
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
```

Also, before adding the view to WindowManager, you need to check if the device support overlay and required permission

```kotlin
private fun checkOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
!Settings.canDrawOverlays(this)
) {
// Get permission first on Android M & above
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
startActivityForResult(intent, 1234)
} else {
// overlayView & params are global variable
windowManager.addView(overlayView, params)
}
}
```

Check the example here: [Kotlin](https://github.com/hyuwah/DraggableView/blob/master/example/src/main/java/io/github/hyuwah/draggableview/OverlayDraggableActivity.kt)

---
## Accompanying Article

* [Implementasi DraggableView di Android (Bahasa)](https://medium.com/@hyuwah/implementasi-draggable-view-di-android-eb84e50fbba9)
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.3.71'
repositories {
google()
jcenter()

}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

package io.github.hyuwah.draggableviewlib

import android.graphics.PixelFormat
import android.os.Build
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import io.github.hyuwah.draggableviewlib.Draggable.DRAG_TOLERANCE
import io.github.hyuwah.draggableviewlib.Draggable.DURATION_MILLIS
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt

@JvmOverloads
fun View.makeDraggable(
Expand Down Expand Up @@ -138,4 +142,67 @@ fun View.makeDraggable(
}
true
}
}

/**
* Make floating draggable overlay view (on top of other application).
* You still have to manually manage adding, updating & removing the view via Window Manager from where
* you call this function
* @param listener callback for new LayoutParams, do `windowManager.updateViewLayout()` here
* @param layoutParams if you need to customize the layout params (e.g. Gravity),
* note that you still have to use correct Layout Flag, can be omitted for default value
* @return layoutParams to be used when adding the view on window manager (outside this function)
*/
@JvmOverloads
fun View.makeOverlayDraggable(
listener: OverlayDraggableListener,
layoutParams: WindowManager.LayoutParams? = null
): WindowManager.LayoutParams {
var widgetInitialX = 0
var widgetDX = 0f
var widgetInitialY = 0
var widgetDY = 0f

val layoutFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
val params = layoutParams
?: WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
layoutFlag,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)

setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
widgetInitialX = params.x
widgetInitialY = params.y
widgetDX = widgetInitialX - event.rawX
widgetDY = widgetInitialY - event.rawY
return@setOnTouchListener true
}
MotionEvent.ACTION_MOVE -> {
val newX = event.rawX + widgetDX
val newY = event.rawY + widgetDY
params.x = newX.roundToInt()
params.y = newY.roundToInt()
listener.onParamsChanged(params)
return@setOnTouchListener true
}
MotionEvent.ACTION_UP -> {
if (abs(params.x - widgetInitialX) <= DRAG_TOLERANCE && abs(params.y - widgetInitialY) <= DRAG_TOLERANCE) {
performClick()
}
return@setOnTouchListener true
}
else -> return@setOnTouchListener false
}
}

return params
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.hyuwah.draggableviewlib

import android.view.WindowManager

interface OverlayDraggableListener {
fun onParamsChanged(updatedParams: WindowManager.LayoutParams)
}
11 changes: 10 additions & 1 deletion example/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.hyuwah.draggableview">

<!-- These permission is only used by overlay draggable view-->
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".JavaMainActivity"></activity>
<activity
android:name=".OverlayDraggableActivity"
android:parentActivityName=".MainActivity" />
<activity
android:name=".JavaMainActivity"
android:parentActivityName=".MainActivity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package io.github.hyuwah.draggableview;

import android.support.v4.view.ViewPropertyAnimatorListener;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
Expand All @@ -20,6 +19,7 @@ public class JavaMainActivity extends AppCompatActivity implements DraggableList
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java_main);
setTitle("Java Activity");

Button button = findViewById(R.id.btn_java);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ class MainActivity : AppCompatActivity() {
// Set click listener
dvTest.setOnClickListener {
Toast.makeText(this@MainActivity, "Clicked", Toast.LENGTH_SHORT).show()
startActivity(Intent(this, JavaMainActivity::class.java))
}

ll_test_draggable.setOnClickListener {
Expand All @@ -130,5 +129,13 @@ class MainActivity : AppCompatActivity() {
.show()
}

btn_java_activity.setOnClickListener {
startActivity(Intent(this, JavaMainActivity::class.java))
}

btn_overlay_activity.setOnClickListener {
startActivity(Intent(this, OverlayDraggableActivity::class.java))
}

}
}
Loading

0 comments on commit f97cbf7

Please sign in to comment.