├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ks │ │ └── controlcenter │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ks │ │ │ └── control │ │ │ ├── AlarmPowerManager.java │ │ │ ├── AnimationUtil.java │ │ │ ├── CtrlCenterWindowBigView.java │ │ │ ├── CtrlCenterWindowService.java │ │ │ ├── CtrlCenterWindowSmallView.java │ │ │ ├── FlashLightManager.java │ │ │ ├── FloatWindowBigView.java │ │ │ ├── FloatWindowService.java │ │ │ ├── FloatWindowSmallView.java │ │ │ ├── HomeKeyEventBroadCastReceiver.java │ │ │ ├── HomeWatcherReceiver.java │ │ │ ├── MainActivity.java │ │ │ ├── MyWindowManager.java │ │ │ ├── SettingActivity.java │ │ │ └── WebActivity.java │ └── res │ │ ├── animator │ │ ├── indicator_animator.xml │ │ ├── indicator_animator_reverse.xml │ │ └── scale_with_alpha.xml │ │ ├── drawable │ │ ├── alertdialog_bg.xml │ │ ├── alertdialog_edit_bg.xml │ │ ├── floating_bg.xml │ │ ├── floating_small_bg.xml │ │ └── floating_small_btn.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_setting.xml │ │ ├── activity_web.xml │ │ ├── floating_ctrl_center_window_big.xml │ │ ├── floating_ctrl_center_window_small.xml │ │ ├── floating_window_big.xml │ │ └── floating_window_small.xml │ │ ├── mipmap-xhdpi │ │ ├── ctrl_center_icon_camara.png │ │ ├── ctrl_center_icon_text.png │ │ ├── ctrl_center_icon_voice.png │ │ ├── floating_icon_camara.png │ │ ├── floating_icon_home.png │ │ ├── floating_icon_text.png │ │ ├── floating_icon_voice.png │ │ ├── ic_find_next_holo_light.png │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── public.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── preferences.xml │ └── test │ └── java │ └── com │ └── ks │ └── controlcenter │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ControlCenter 2 | 安卓仿iPhone控制中心,上划出现控制中心工具栏 3 | Android 控制中心 4 | # 关于应用 5 | Android 控制中心是一款『超轻量级』的便捷工具应用 6 | 纯粹,拒绝『后台工作』,不随意消耗 电量 7 | 节能,拒绝『后台连网』,不任意消耗 流量 8 | 简洁至极,『727K』安装包,仅一张图片大小 9 | # 一触即发 10 | 快速,『9 毫秒』极速启动,内存消耗也极少 11 | 便捷,『一步直达』开关设置、参数调整、应用启动 12 | 手机底部上划/左右边缘触摸/双击Home键,即划出控制面板 13 | # 手电筒 14 | 弹出面板,强光手电一触即发,手电筒瞬间点亮你的黑夜 15 | # 扫一扫 16 | 划出面板,二维码、条形码轻松扫,一点即开 17 | 支持打开网页、下载、搜索 18 | 支持扫描wifi设置并直接关联 19 | 支持文本转换二维码并分享出去 20 | 支持地图显示地理位置、经纬度 21 | 支持解析名片,新建联系人,拨打电话、发送短信 22 | # 快捷开关 23 | 网络、蓝牙、定位、震动模式、飞行模式、竖屏模式、勿扰模式、省电模式等 24 | # 快速调节 25 | 调节屏幕亮度,调节声音大小 26 | 铃声、音视频、系统、通知四种声音同步调整一步到位 27 | 声音同步调整,避免铃声静音而视频却大声播放的尴尬 28 | # 一步直达 29 | 一步拉起支付宝扫码付款 30 | 一步直达系统录音器、闹钟、照相机 31 | # 升级规划 32 | 音乐控制 33 | 自定义功能 34 | 常用联系人 35 | 应用便捷操作 36 | 应用截图 37 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "com.ks.control" 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | compile 'com.nineoldandroids:library:2.4.0' 30 | testCompile 'junit:junit:4.12' 31 | } 32 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Android\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ks/controlcenter/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ks.controlcenter; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.ks.controlcenter", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 41 | 42 | 45 | 46 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/AlarmPowerManager.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.util.Log; 8 | 9 | import java.io.DataOutputStream; 10 | import java.io.IOException; 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | * Created by Admin on 2017/6/16 0016 11:21. 15 | * Author: kang 16 | * Email: kangsafe@163.com 17 | */ 18 | 19 | public class AlarmPowerManager { 20 | 21 | public static void rebootBroadcast(Context context) { 22 | Intent i = new Intent(Intent.ACTION_REBOOT); 23 | i.putExtra("nowait", 1); 24 | i.putExtra("interval", 1); 25 | i.putExtra("window", 0); 26 | context.sendBroadcast(i); 27 | } 28 | 29 | public static void powerOffAlarm(Context context) { 30 | Intent intent = new Intent( 31 | "com.android.settings.action.REQUEST_POWER_OFF"); 32 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 33 | intent, PendingIntent.FLAG_CANCEL_CURRENT); 34 | AlarmManager am = (AlarmManager) context 35 | .getSystemService(Context.ALARM_SERVICE); 36 | am.set(AlarmManager.RTC_WAKEUP, 0, pendingIntent); 37 | } 38 | 39 | public static void powerOff(Context context) { 40 | Process process; 41 | try { 42 | process = Runtime.getRuntime().exec("su"); 43 | DataOutputStream out = new DataOutputStream( 44 | process.getOutputStream()); 45 | out.writeBytes("reboot -p\n"); 46 | out.writeBytes("exit\n"); 47 | out.flush(); 48 | } catch (IOException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | public static void reBoot() { 54 | try { 55 | Process proc = Runtime.getRuntime().exec("su -c \"/system/bin/reboot\""); 56 | proc.waitFor(); 57 | 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | 62 | } 63 | 64 | public static void powerOff() { 65 | try { 66 | 67 | //获得ServiceManager类 68 | Class ServiceManager = Class 69 | .forName("android.os.ServiceManager"); 70 | 71 | //获得ServiceManager的getService方法 72 | Method getService = ServiceManager.getMethod("getService", java.lang.String.class); 73 | 74 | //调用getService获取RemoteService 75 | Object oRemoteService = getService.invoke(null, Context.POWER_SERVICE); 76 | 77 | //获得IPowerManager.Stub类 78 | Class cStub = Class 79 | .forName("android.os.IPowerManager$Stub"); 80 | //获得asInterface方法 81 | Method asInterface = cStub.getMethod("asInterface", android.os.IBinder.class); 82 | //调用asInterface方法获取IPowerManager对象 83 | Object oIPowerManager = asInterface.invoke(null, oRemoteService); 84 | //获得shutdown()方法 85 | Method shutdown = oIPowerManager.getClass().getMethod("shutdown", boolean.class, boolean.class); 86 | //调用shutdown()方法 87 | shutdown.invoke(oIPowerManager, false, true); 88 | 89 | } catch (Exception e) { 90 | Log.e("powerOff", e.toString(), e); 91 | } 92 | } 93 | 94 | public static void bcShutdown(Context context) { 95 | Log.v("TAG", "broadcast->shutdown"); 96 | Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN"); 97 | intent.putExtra("android.intent.extra.KEY_CONFIRM", false); 98 | //其中false换成true,会弹出是否关机的确认窗口 99 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 100 | context.startActivity(intent); 101 | } 102 | 103 | public static void bcReboot(Context context) { 104 | Log.v("TAG", "broadcast->reboot"); 105 | Intent intent2 = new Intent(Intent.ACTION_REBOOT); 106 | intent2.putExtra("nowait", 1); 107 | intent2.putExtra("interval", 1); 108 | intent2.putExtra("window", 0); 109 | context.sendBroadcast(intent2); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/AnimationUtil.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.view.animation.Animation; 4 | import android.view.animation.TranslateAnimation; 5 | 6 | /** 7 | * Created by Admin on 2017/6/14 0014 13:50. 8 | * Author: kang 9 | * Email: kangsafe@163.com 10 | */ 11 | 12 | public class AnimationUtil { 13 | private static final String TAG = AnimationUtil.class.getSimpleName(); 14 | 15 | /** 16 | * 从控件所在位置移动到控件的底部 17 | * 18 | * @return 19 | */ 20 | public static TranslateAnimation moveToViewBottom() { 21 | TranslateAnimation mHiddenAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, 22 | Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 23 | 0.0f, Animation.RELATIVE_TO_SELF, 1.0f); 24 | mHiddenAction.setDuration(500); 25 | return mHiddenAction; 26 | } 27 | 28 | /** 29 | * 从控件的底部移动到控件所在位置 30 | * 31 | * @return 32 | */ 33 | public static TranslateAnimation moveToViewLocation() { 34 | TranslateAnimation mHiddenAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, 35 | Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 36 | 1.0f, Animation.RELATIVE_TO_SELF, 0.0f); 37 | mHiddenAction.setDuration(500); 38 | return mHiddenAction; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/CtrlCenterWindowBigView.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Handler; 6 | import android.util.Log; 7 | import android.view.KeyEvent; 8 | import android.view.LayoutInflater; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.animation.AnimationUtils; 12 | import android.widget.LinearLayout; 13 | 14 | import com.nineoldandroids.animation.ObjectAnimator; 15 | 16 | /** 17 | * Created by Admin on 2017/6/13 0013 09:36. 18 | * Author: kang 19 | * Email: kangsafe@163.com 20 | */ 21 | public class CtrlCenterWindowBigView extends LinearLayout { 22 | View big; 23 | 24 | public CtrlCenterWindowBigView(final Context context) { 25 | super(context); 26 | View view = LayoutInflater.from(context).inflate(R.layout.floating_ctrl_center_window_big, this); 27 | big = view.findViewById(R.id.ctrl_center_big_container); 28 | big.setAnimation(AnimationUtil.moveToViewLocation()); 29 | //拍照 30 | findViewById(R.id.floating_camara).setOnClickListener(new OnClickListener() { 31 | @Override 32 | public void onClick(View v) { 33 | // Intent intent = new Intent(context, EditDocumentActivity.class); 34 | // intent.putExtra(NoteActivity.FAB_ACTION, R.id.action_markdown_take_photo); 35 | // intent.putExtra(ExtraCode.GroupId, ""); 36 | // context.startActivity(intent); 37 | back(); 38 | } 39 | }); 40 | //文本 41 | findViewById(R.id.floating_text).setOnClickListener(new OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | // Intent intent = new Intent(context, EditDocumentActivity.class); 45 | // intent.putExtra(ExtraCode.GroupId, ""); 46 | // context.startActivity(intent); 47 | back(); 48 | } 49 | }); 50 | //语音 51 | findViewById(R.id.floating_voice).setOnClickListener(new OnClickListener() { 52 | @Override 53 | public void onClick(View v) { 54 | back(); 55 | } 56 | }); 57 | } 58 | 59 | private void back() { 60 | ObjectAnimator animator = ObjectAnimator.ofFloat(big, "translationY", 0, big.getHeight()); 61 | animator.setDuration(500); 62 | animator.start(); 63 | animator.setInterpolator(AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.linear)); 64 | // big.setAnimation(animator); 65 | // big.setAnimation(AnimationUtil.moveToViewBottom()); 66 | // big.setVisibility(GONE); 67 | // ViewHelper.setTranslationY(big, big.getHeight()); 68 | new Handler().postDelayed(new Runnable() { 69 | @Override 70 | public void run() { 71 | MyWindowManager.removeCtrlCenterBigWindow(getContext()); 72 | MyWindowManager.createCtrlCenterSmallWindow(getContext()); 73 | } 74 | }, 500); 75 | } 76 | 77 | @Override 78 | public boolean dispatchKeyEvent(KeyEvent event) { 79 | Log.i("Key", big.getVisibility() + ":" + event.getAction() + ":" + event.getKeyCode()); 80 | if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { 81 | back(); 82 | } 83 | return super.dispatchKeyEvent(event); 84 | } 85 | 86 | @Override 87 | public boolean dispatchTouchEvent(MotionEvent event) { 88 | Log.i("Key", "Visiblity:" + big.getVisibility() + ",Action:" + event.getAction() + 89 | ",RawX:" + event.getRawX() + ",RawY:" + event.getRawY() + ",X:" + event.getX() + ",Y:" + event.getY()); 90 | // if (event.getAction() == MotionEvent.ACTION_DOWN) { 91 | // Notify touch outside listener if user tapped outside a given view 92 | // Rect viewRect = new Rect(); 93 | // big.getGlobalVisibleRect(viewRect); 94 | // if (!viewRect.contains((int) event.getRawX(), (int) event.getRawY())) { 95 | // back(); 96 | // } 97 | // } 98 | if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { 99 | back(); 100 | return true; 101 | } 102 | return super.dispatchTouchEvent(event); 103 | } 104 | // 105 | // public void onAttachedToWindow() { 106 | // getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD); 107 | // super.onAttachedToWindow(); 108 | // } 109 | 110 | // public void setOnTouchOutsideViewListener(View view, OnTouchOutsideViewListener onTouchOutsideViewListener) { 111 | // mTouchOutsideView = view; 112 | // mOnTouchOutsideViewListener = onTouchOutsideViewListener; 113 | // } 114 | // 115 | // public OnTouchOutsideViewListener getOnTouchOutsideViewListener() { 116 | // return mOnTouchOutsideViewListener; 117 | // } 118 | 119 | // @Override 120 | // public boolean dispatchTouchEvent(final MotionEvent ev) { 121 | // if (ev.getAction() == MotionEvent.ACTION_DOWN) { 122 | // // Notify touch outside listener if user tapped outside a given view 123 | // if (mOnTouchOutsideViewListener != null && mTouchOutsideView != null 124 | // && mTouchOutsideView.getVisibility() == View.VISIBLE) { 125 | // Rect viewRect = new Rect(); 126 | // mTouchOutsideView.getGlobalVisibleRect(viewRect); 127 | // if (!viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) { 128 | // mOnTouchOutsideViewListener.onTouchOutside(mTouchOutsideView, ev); 129 | // } 130 | // } 131 | // } 132 | // return super.dispatchTouchEvent(ev); 133 | // } 134 | 135 | // /** 136 | // * Interface definition for a callback to be invoked when a touch event has occurred outside a formerly specified 137 | // * view. See {@link #setOnTouchOutsideViewListener(View, OnTouchOutsideViewListener).} 138 | // */ 139 | // public interface OnTouchOutsideViewListener { 140 | // 141 | // /** 142 | // * Called when a touch event has occurred outside a given view. 143 | // * 144 | // * @param view The view that has not been touched. 145 | // * @param event The MotionEvent object containing full information about the event. 146 | // */ 147 | // void onTouchOutside(View view, MotionEvent event); 148 | // } 149 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/CtrlCenterWindowService.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.app.ActivityManager; 4 | import android.app.Service; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.content.pm.ResolveInfo; 9 | import android.os.IBinder; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by Admin on 2017/6/13 0013 09:34. 16 | * Author: kang 17 | * Email: kangsafe@163.com 18 | */ 19 | 20 | public class CtrlCenterWindowService extends Service { 21 | 22 | @Override 23 | public IBinder onBind(Intent intent) { 24 | return null; 25 | } 26 | 27 | @Override 28 | public int onStartCommand(Intent intent, int flags, int startId) { 29 | MyWindowManager.createCtrlCenterSmallWindow(getApplicationContext()); 30 | return super.onStartCommand(intent, flags, startId); 31 | } 32 | 33 | @Override 34 | public void onDestroy() { 35 | super.onDestroy(); 36 | } 37 | 38 | /** 39 | * 判断当前界面是否是桌面 40 | */ 41 | private boolean isHome() { 42 | ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 43 | List rti = mActivityManager.getRunningTasks(1); 44 | return getHomes().contains(rti.get(0).topActivity.getPackageName()); 45 | } 46 | 47 | /** 48 | * 获得属于桌面的应用的应用包名称 49 | * 50 | * @return 返回包含所有包名的字符串列表 51 | */ 52 | private List getHomes() { 53 | List names = new ArrayList(); 54 | PackageManager packageManager = this.getPackageManager(); 55 | Intent intent = new Intent(Intent.ACTION_MAIN); 56 | intent.addCategory(Intent.CATEGORY_HOME); 57 | List resolveInfo = packageManager.queryIntentActivities(intent, 58 | PackageManager.MATCH_DEFAULT_ONLY); 59 | for (ResolveInfo ri : resolveInfo) { 60 | names.add(ri.activityInfo.packageName); 61 | } 62 | return names; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/CtrlCenterWindowSmallView.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | import android.view.WindowManager; 8 | import android.widget.LinearLayout; 9 | 10 | /** 11 | * Created by Admin on 2017/6/13 0013 09:36. 12 | * Author: kang 13 | * Email: kangsafe@163.com 14 | */ 15 | public class CtrlCenterWindowSmallView extends LinearLayout { 16 | 17 | /** 18 | * 用于更新小悬浮窗的位置 19 | */ 20 | private WindowManager windowManager; 21 | 22 | /** 23 | * 记录当前手指位置在屏幕上的纵坐标值 24 | */ 25 | private float yInScreen; 26 | 27 | /** 28 | * 记录手指按下时在屏幕上的纵坐标的值 29 | */ 30 | private float yDownInScreen; 31 | 32 | /** 33 | * 小悬浮窗的参数 34 | */ 35 | private WindowManager.LayoutParams mParams; 36 | 37 | public CtrlCenterWindowSmallView(final Context context) { 38 | super(context); 39 | windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 40 | View view = LayoutInflater.from(context).inflate(R.layout.floating_ctrl_center_window_small, this); 41 | } 42 | 43 | @Override 44 | public boolean onTouchEvent(MotionEvent event) { 45 | switch (event.getAction()) { 46 | case MotionEvent.ACTION_DOWN: 47 | // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度 48 | yDownInScreen = event.getRawY(); 49 | break; 50 | case MotionEvent.ACTION_MOVE: 51 | yInScreen = event.getRawY(); 52 | updateViewPosition(); 53 | break; 54 | case MotionEvent.ACTION_UP: 55 | // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。 56 | if (yDownInScreen - yInScreen > 5) { 57 | openBigWindow(); 58 | } 59 | yDownInScreen = yInScreen; 60 | updateViewPosition(); 61 | break; 62 | default: 63 | break; 64 | } 65 | return true; 66 | } 67 | 68 | /** 69 | * 打开大悬浮窗,同时关闭小悬浮窗。 70 | */ 71 | private void openBigWindow() { 72 | MyWindowManager.removeCtrlCenterSmallWindow(getContext()); 73 | MyWindowManager.createCtrlCenterBigWindow(getContext()); 74 | } 75 | 76 | /** 77 | * 更新小悬浮窗在屏幕中的位置。 78 | */ 79 | private void updateViewPosition() { 80 | mParams.x = 0; 81 | mParams.y = (int) (yDownInScreen - yInScreen); 82 | windowManager.updateViewLayout(this, mParams); 83 | } 84 | 85 | /** 86 | * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。 87 | * 88 | * @param params 小悬浮窗的参数 89 | */ 90 | public void setParams(WindowManager.LayoutParams params) { 91 | mParams = params; 92 | } 93 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/FlashLightManager.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | /** 4 | * Created by Admin on 2017/6/16 0016 09:01. 5 | * Author: kang 6 | * Email: kangsafe@163.com 7 | */ 8 | 9 | import android.Manifest; 10 | import android.content.Context; 11 | import android.content.pm.FeatureInfo; 12 | import android.content.pm.PackageManager; 13 | import android.graphics.SurfaceTexture; 14 | import android.hardware.Camera; 15 | import android.hardware.camera2.CameraCaptureSession; 16 | import android.hardware.camera2.CameraCharacteristics; 17 | import android.hardware.camera2.CameraDevice; 18 | import android.hardware.camera2.CameraManager; 19 | import android.hardware.camera2.CameraMetadata; 20 | import android.hardware.camera2.CaptureRequest; 21 | import android.hardware.camera2.params.StreamConfigurationMap; 22 | import android.os.Build; 23 | import android.support.v4.app.ActivityCompat; 24 | import android.util.Log; 25 | import android.view.Surface; 26 | import android.widget.Toast; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | /** 32 | * 闪光灯管理工具类 33 | * 34 | * @author linzhiyong 35 | * @time 2016年11月17日07:47:03 36 | * @email wflinzhiyong@163.com 37 | * @desc * 如果配合拍照使用, 则无需调用init()方法, 直接使用turnLightOnCamera(Camera c) turnLightOffCamera(Camera c) 38 | * * 如果只作为手电筒使用, 则需要初始化init()方法, 使用turnOn() turnOff() 39 | */ 40 | public class FlashLightManager { 41 | 42 | private static final String TAG = FlashLightManager.class.getName(); 43 | 44 | /** 45 | * 上下文对象 46 | */ 47 | private Context context; 48 | 49 | /** 50 | * 是否已经开启闪光灯 51 | */ 52 | private boolean isOpenFlash = false; 53 | 54 | /** 55 | * Camera相机硬件操作类 56 | */ 57 | private Camera camera = null; 58 | 59 | /** 60 | * Camera2相机硬件操作类 61 | */ 62 | private CameraManager manager = null; 63 | private CameraDevice cameraDevice; 64 | private CameraCaptureSession captureSession = null; 65 | private CaptureRequest request = null; 66 | private SurfaceTexture surfaceTexture; 67 | private Surface surface; 68 | private String cameraId = null; 69 | private boolean isSupportFlashCamera2 = false; 70 | 71 | private FlashLightManager() { 72 | } 73 | 74 | public FlashLightManager(Context context) { 75 | this.context = context; 76 | } 77 | 78 | /** 79 | * 初始化相机 80 | */ 81 | public void init() { 82 | this.manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 83 | if (isLOLLIPOP()) { 84 | initCamera2(); 85 | } else { 86 | camera = Camera.open(); 87 | } 88 | } 89 | 90 | /** 91 | * 开启闪光灯 92 | */ 93 | public void turnOn() { 94 | if (!isSupportFlash()) { 95 | showToast("设备不支持闪光灯!"); 96 | return; 97 | } 98 | if (isOpenFlash) { 99 | return; 100 | } 101 | 102 | if (isLOLLIPOP()) { 103 | turnLightOnCamera2(); 104 | } else { 105 | turnLightOnCamera(camera); 106 | } 107 | } 108 | 109 | /** 110 | * 关闭闪光灯 111 | */ 112 | public void turnOff() { 113 | if (!isSupportFlash()) { 114 | showToast("设备不支持闪光灯!"); 115 | return; 116 | } 117 | if (!isOpenFlash) { 118 | return; 119 | } 120 | if (isLOLLIPOP()) { 121 | turnLightOffCamera2(); 122 | } else { 123 | turnLightOffCamera(camera); 124 | } 125 | isOpenFlash = false; 126 | } 127 | 128 | /** 129 | * 开启Camera2闪光灯 130 | */ 131 | private void turnLightOnCamera2() { 132 | new Object() { 133 | private void _turnLightOnCamera2() { 134 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 135 | if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { 136 | showToast("应用未开启访问相机权限!"); 137 | return; 138 | } 139 | try { 140 | manager.openCamera(cameraId, new CameraDevice.StateCallback() { 141 | 142 | @Override 143 | public void onOpened(CameraDevice camera) { 144 | cameraDevice = camera; 145 | createCaptureSession(); 146 | } 147 | 148 | @Override 149 | public void onError(CameraDevice camera, int error) { 150 | } 151 | 152 | @Override 153 | public void onDisconnected(CameraDevice camera) { 154 | } 155 | }, null); 156 | } catch (Exception e) { 157 | Log.e(TAG, e.getMessage(), e); 158 | showToast("开启失败:" + e.getMessage()); 159 | } 160 | } 161 | } 162 | }._turnLightOnCamera2(); 163 | } 164 | 165 | /** 166 | * 关闭Camera2闪光灯 167 | */ 168 | private void turnLightOffCamera2() { 169 | new Object() { 170 | private void _turnLightOffCamera2() { 171 | if (cameraDevice != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 172 | cameraDevice.close(); 173 | } 174 | } 175 | }._turnLightOffCamera2(); 176 | } 177 | 178 | /** 179 | * 判断设备是否支持闪光灯 180 | * 181 | * @return boolean 182 | */ 183 | public boolean isSupportFlash() { 184 | if (isLOLLIPOP()) { // 判断当前Android系统版本是否 >= 21, 分别处理 185 | return isSupportFlashCamera2; 186 | } else { 187 | PackageManager pm = context.getPackageManager(); 188 | FeatureInfo[] features = pm.getSystemAvailableFeatures(); 189 | for (FeatureInfo f : features) { 190 | // 判断设备是否支持闪光灯 191 | if (PackageManager.FEATURE_CAMERA_FLASH.equals(f.name)) { 192 | return true; 193 | } 194 | } 195 | // 判断是否支持闪光灯,方式二 196 | // Camera.Parameters parameters = camera.getParameters(); 197 | // if (parameters == null) { 198 | // return false; 199 | // } 200 | // List flashModes = parameters.getSupportedFlashModes(); 201 | // if (flashModes == null) { 202 | // return false; 203 | // } 204 | } 205 | return false; 206 | } 207 | 208 | /** 209 | * 是否已经开启闪光灯 210 | * 211 | * @return 212 | */ 213 | public boolean isTurnOnFlash() { 214 | return isOpenFlash; 215 | } 216 | 217 | /** 218 | * 判断Android系统版本是否 >= LOLLIPOP(API21) 219 | * 220 | * @return boolean 221 | */ 222 | private boolean isLOLLIPOP() { 223 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 224 | return true; 225 | } else { 226 | return false; 227 | } 228 | } 229 | 230 | /** 231 | * 通过设置Camera打开闪光灯 232 | * 233 | * @param mCamera 234 | */ 235 | public void turnLightOnCamera(Camera mCamera) { 236 | Camera.Parameters parameters = mCamera.getParameters(); 237 | List flashModes = parameters.getSupportedFlashModes(); 238 | String flashMode = parameters.getFlashMode(); 239 | if (!Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) { 240 | // 开启闪光灯 241 | if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { 242 | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 243 | mCamera.setParameters(parameters); 244 | } 245 | } 246 | isOpenFlash = true; 247 | } 248 | 249 | /** 250 | * 通过设置Camera关闭闪光灯 251 | * 252 | * @param mCamera 253 | */ 254 | public void turnLightOffCamera(Camera mCamera) { 255 | Camera.Parameters parameters = mCamera.getParameters(); 256 | List flashModes = parameters.getSupportedFlashModes(); 257 | String flashMode = parameters.getFlashMode(); 258 | if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) { 259 | // 关闭闪光灯 260 | if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) { 261 | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); 262 | mCamera.setParameters(parameters); 263 | } 264 | } 265 | isOpenFlash = false; 266 | } 267 | 268 | /** 269 | * 初始化Camera2 270 | */ 271 | private void initCamera2() { 272 | new Object() { 273 | private void _initCamera2() { 274 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 275 | try { 276 | for (String _cameraId : manager.getCameraIdList()) { 277 | CameraCharacteristics characteristics = manager.getCameraCharacteristics(_cameraId); 278 | // 过滤掉前置摄像头 279 | Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 280 | if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { 281 | continue; 282 | } 283 | StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 284 | if (map == null) { 285 | continue; 286 | } 287 | cameraId = _cameraId; 288 | // 判断设备是否支持闪光灯 289 | isSupportFlashCamera2 = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 290 | } 291 | } catch (Exception e) { 292 | Log.e(TAG, e.getMessage(), e); 293 | showToast("初始化失败:" + e.getMessage()); 294 | } 295 | } 296 | } 297 | }._initCamera2(); 298 | } 299 | 300 | /** 301 | * createCaptureSession 302 | */ 303 | private void createCaptureSession() { 304 | new Object() { 305 | private void _createCaptureSession() { 306 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 307 | final CameraCaptureSession.StateCallback stateCallback = new CameraCaptureSession.StateCallback() { 308 | 309 | public void onConfigured(CameraCaptureSession arg0) { 310 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 311 | captureSession = arg0; 312 | CaptureRequest.Builder builder; 313 | try { 314 | builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 315 | builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH); 316 | builder.addTarget(surface); 317 | request = builder.build(); 318 | captureSession.capture(request, null, null); 319 | isOpenFlash = true; 320 | } catch (Exception e) { 321 | Log.e(TAG, e.getMessage(), e); 322 | showToast("开启失败:" + e.getMessage()); 323 | } 324 | } 325 | } 326 | 327 | public void onConfigureFailed(CameraCaptureSession arg0) { 328 | } 329 | }; 330 | 331 | surfaceTexture = new SurfaceTexture(0, false); 332 | surfaceTexture.setDefaultBufferSize(1280, 720); 333 | surface = new Surface(surfaceTexture); 334 | ArrayList localArrayList = new ArrayList(1); 335 | localArrayList.add(surface); 336 | try { 337 | cameraDevice.createCaptureSession(localArrayList, stateCallback, null); 338 | } catch (Exception e) { 339 | Log.e(TAG, e.getMessage(), e); 340 | showToast("开启失败:" + e.getMessage()); 341 | } 342 | } 343 | } 344 | }._createCaptureSession(); 345 | } 346 | 347 | private void showToast(String content) { 348 | Toast.makeText(context, content, Toast.LENGTH_LONG).show(); 349 | } 350 | 351 | } 352 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/FloatWindowBigView.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.ImageView; 9 | import android.widget.LinearLayout; 10 | 11 | /** 12 | * Created by Admin on 2017/6/13 0013 09:37. 13 | * Author: kang 14 | * Email: kangsafe@163.com 15 | */ 16 | 17 | public class FloatWindowBigView extends LinearLayout { 18 | 19 | /** 20 | * 记录大悬浮窗的宽度 21 | */ 22 | public static int viewWidth; 23 | 24 | /** 25 | * 记录大悬浮窗的高度 26 | */ 27 | public static int viewHeight; 28 | private Context context; 29 | 30 | public FloatWindowBigView(final Context context) { 31 | super(context); 32 | this.context = context; 33 | LayoutInflater.from(context).inflate(R.layout.floating_window_big, this); 34 | View view = findViewById(R.id.big_window_layout); 35 | viewWidth = view.getLayoutParams().width; 36 | viewHeight = view.getLayoutParams().height; 37 | Button close = (Button) findViewById(R.id.close); 38 | final Button back = (Button) findViewById(R.id.back); 39 | ImageView camara = (ImageView) findViewById(R.id.floating_camara); 40 | ImageView text = (ImageView) findViewById(R.id.floating_text); 41 | ImageView home = (ImageView) findViewById(R.id.floating_home); 42 | ImageView voice = (ImageView) findViewById(R.id.floating_voice); 43 | close.setOnClickListener(new OnClickListener() { 44 | @Override 45 | public void onClick(View v) { 46 | back(); 47 | Intent intent = new Intent(getContext(), FloatWindowService.class); 48 | context.stopService(intent); 49 | } 50 | }); 51 | back.setOnClickListener(new OnClickListener() { 52 | @Override 53 | public void onClick(View v) { 54 | back(); 55 | } 56 | }); 57 | camara.setOnClickListener(new OnClickListener() { 58 | @Override 59 | public void onClick(View v) { 60 | // Intent intent = new Intent(context, EditDocumentActivity.class); 61 | // intent.putExtra(NoteActivity.FAB_ACTION, R.id.action_markdown_take_photo); 62 | // intent.putExtra(ExtraCode.GroupId, ""); 63 | // context.startActivity(intent); 64 | back(); 65 | } 66 | }); 67 | text.setOnClickListener(new OnClickListener() { 68 | @Override 69 | public void onClick(View v) { 70 | // Intent intent = new Intent(context, EditDocumentActivity.class); 71 | // intent.putExtra(ExtraCode.GroupId, ""); 72 | // context.startActivity(intent); 73 | } 74 | }); 75 | home.setOnClickListener(new OnClickListener() { 76 | @Override 77 | public void onClick(View v) { 78 | back(); 79 | } 80 | }); 81 | voice.setOnClickListener(new OnClickListener() { 82 | @Override 83 | public void onClick(View v) { 84 | 85 | } 86 | }); 87 | } 88 | 89 | private void back() { 90 | // 点击返回的时候,移除大悬浮窗,创建小悬浮窗 91 | MyWindowManager.removeBigWindow(context); 92 | MyWindowManager.createSmallWindow(context); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/FloatWindowService.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.app.ActivityManager; 4 | import android.app.Service; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.content.pm.ResolveInfo; 9 | import android.os.Handler; 10 | import android.os.IBinder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Timer; 15 | import java.util.TimerTask; 16 | 17 | /** 18 | * Created by Admin on 2017/6/13 0013 09:34. 19 | * Author: kang 20 | * Email: kangsafe@163.com 21 | */ 22 | 23 | public class FloatWindowService extends Service { 24 | 25 | /** 26 | * 用于在线程中创建或移除悬浮窗。 27 | */ 28 | private Handler handler = new Handler(); 29 | 30 | /** 31 | * 定时器,定时进行检测当前应该创建还是移除悬浮窗。 32 | */ 33 | private Timer timer; 34 | 35 | @Override 36 | public IBinder onBind(Intent intent) { 37 | return null; 38 | } 39 | 40 | @Override 41 | public int onStartCommand(Intent intent, int flags, int startId) { 42 | // 开启定时器,每隔0.5秒刷新一次 43 | // if (timer == null) { 44 | // timer = new Timer(); 45 | // timer.scheduleAtFixedRate(new RefreshTask(), 0, 500); 46 | // } 47 | MyWindowManager.createSmallWindow(getApplicationContext()); 48 | return super.onStartCommand(intent, flags, startId); 49 | } 50 | 51 | @Override 52 | public void onDestroy() { 53 | super.onDestroy(); 54 | // Service被终止的同时也停止定时器继续运行 55 | if (timer != null) { 56 | timer.cancel(); 57 | timer = null; 58 | } 59 | } 60 | 61 | class RefreshTask extends TimerTask { 62 | 63 | @Override 64 | public void run() { 65 | // // 当前界面是桌面,且没有悬浮窗显示,则创建悬浮窗。 66 | // if (isHome() && !MyWindowManager.isWindowShowing()) { 67 | // handler.post(new Runnable() { 68 | // @Override 69 | // public void run() { 70 | // MyWindowManager.createSmallWindow(getApplicationContext()); 71 | // } 72 | // }); 73 | // } 74 | // // 当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗。 75 | // else if (!isHome() && MyWindowManager.isWindowShowing()) { 76 | // handler.post(new Runnable() { 77 | // @Override 78 | // public void run() { 79 | // MyWindowManager.removeSmallWindow(getApplicationContext()); 80 | // MyWindowManager.removeBigWindow(getApplicationContext()); 81 | // } 82 | // }); 83 | // } 84 | // // 当前界面是桌面,且有悬浮窗显示,则更新内存数据。 85 | // else if (isHome() && MyWindowManager.isWindowShowing()) { 86 | // handler.post(new Runnable() { 87 | // @Override 88 | // public void run() { 89 | // MyWindowManager.updateUsedPercent(getApplicationContext()); 90 | // } 91 | // }); 92 | // } 93 | } 94 | 95 | } 96 | 97 | /** 98 | * 判断当前界面是否是桌面 99 | */ 100 | private boolean isHome() { 101 | ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 102 | List rti = mActivityManager.getRunningTasks(1); 103 | return getHomes().contains(rti.get(0).topActivity.getPackageName()); 104 | } 105 | 106 | /** 107 | * 获得属于桌面的应用的应用包名称 108 | * 109 | * @return 返回包含所有包名的字符串列表 110 | */ 111 | private List getHomes() { 112 | List names = new ArrayList(); 113 | PackageManager packageManager = this.getPackageManager(); 114 | Intent intent = new Intent(Intent.ACTION_MAIN); 115 | intent.addCategory(Intent.CATEGORY_HOME); 116 | List resolveInfo = packageManager.queryIntentActivities(intent, 117 | PackageManager.MATCH_DEFAULT_ONLY); 118 | for (ResolveInfo ri : resolveInfo) { 119 | names.add(ri.activityInfo.packageName); 120 | } 121 | return names; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/FloatWindowSmallView.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.animation.ObjectAnimator; 5 | import android.app.AlertDialog; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.view.KeyEvent; 9 | import android.view.LayoutInflater; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | import android.view.WindowManager; 13 | import android.widget.ImageView; 14 | import android.widget.LinearLayout; 15 | import android.widget.Toast; 16 | 17 | 18 | import java.lang.reflect.Field; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Created by Admin on 2017/6/13 0013 09:36. 24 | * Author: kang 25 | * Email: kangsafe@163.com 26 | */ 27 | public class FloatWindowSmallView extends LinearLayout implements View.OnClickListener { 28 | 29 | /** 30 | * 记录小悬浮窗的宽度 31 | */ 32 | public static int viewWidth; 33 | 34 | /** 35 | * 记录小悬浮窗的高度 36 | */ 37 | public static int viewHeight; 38 | 39 | /** 40 | * 记录系统状态栏的高度 41 | */ 42 | private static int statusBarHeight; 43 | /** 44 | * 记录系统状态栏的高度 45 | */ 46 | private static int statusBarWidth; 47 | 48 | /** 49 | * 用于更新小悬浮窗的位置 50 | */ 51 | private WindowManager windowManager; 52 | 53 | /** 54 | * 小悬浮窗的参数 55 | */ 56 | private WindowManager.LayoutParams mParams; 57 | 58 | /** 59 | * 记录当前手指位置在屏幕上的横坐标值 60 | */ 61 | private float xInScreen; 62 | 63 | /** 64 | * 记录当前手指位置在屏幕上的纵坐标值 65 | */ 66 | private float yInScreen; 67 | 68 | /** 69 | * 记录手指按下时在屏幕上的横坐标的值 70 | */ 71 | private float xDownInScreen; 72 | 73 | /** 74 | * 记录手指按下时在屏幕上的纵坐标的值 75 | */ 76 | private float yDownInScreen; 77 | 78 | /** 79 | * 记录手指按下时在小悬浮窗的View上的横坐标的值 80 | */ 81 | private float xInView; 82 | 83 | /** 84 | * 记录手指按下时在小悬浮窗的View上的纵坐标的值 85 | */ 86 | private float yInView; 87 | 88 | 89 | private int[] res = {};//{R.id.iv_open, R.id.iv_camera, R.id.iv_music, R.id.iv_place, R.id.iv_sleep, R.id.iv_thought, R.id.iv_with}; 90 | private List imageViewList = new ArrayList(); 91 | private boolean isOpen = false;//菜单是否打开状态 92 | private final int r = 400;//扇形半径 93 | private float angle;//按钮之间的夹角 94 | private final long intervalTime = 100; //菜单展开的时间间隔 95 | 96 | public FloatWindowSmallView(Context context) { 97 | super(context); 98 | windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 99 | LayoutInflater.from(context).inflate(R.layout.floating_window_small, this); 100 | View view = findViewById(R.id.small_window_layout); 101 | viewWidth = view.getLayoutParams().width; 102 | viewHeight = view.getLayoutParams().height; 103 | // TextView percentView = (TextView) findViewById(R.id.percent); 104 | // percentView.setText(MyWindowManager.getUsedPercentValue(context)); 105 | // initImageView(); 106 | // 计算按钮之间的夹角 107 | // angle = (float) Math.PI / (2 * (res.length - 1)); 108 | statusBarWidth = windowManager.getDefaultDisplay().getWidth(); 109 | } 110 | 111 | /** 112 | * 初始化View 113 | */ 114 | private void initImageView() { 115 | ImageView imageView = null; 116 | for (int i = 0; i < res.length; i++) { 117 | imageView = (ImageView) findViewById(res[i]); 118 | imageView.setOnClickListener(this); 119 | imageViewList.add(imageView); 120 | } 121 | } 122 | 123 | /** 124 | * 点击事件 125 | */ 126 | @Override 127 | public void onClick(View view) { 128 | switch (view.getId()) { 129 | // case R.id.iv_open: 130 | // if (isOpen) { 131 | // closeAnim(); 132 | // } else { 133 | // startAnim(); 134 | // } 135 | // break; 136 | default: 137 | // Toast.makeText(MyApplication.getContext(), "" + view.getId(), Toast.LENGTH_SHORT).show(); 138 | break; 139 | } 140 | } 141 | 142 | /** 143 | * 展开菜单 144 | */ 145 | private void startAnim() { 146 | ObjectAnimator animatorX = null; 147 | ObjectAnimator animatorY = null; 148 | float translationX;//横坐标偏移距离 149 | float translationY;//纵坐标偏移距离 150 | //左上角 151 | if (mParams.x == 0 && mParams.y == 0) { 152 | for (int i = 0; i < res.length; i++) { 153 | translationX = (float) (r * Math.sin(i * 90 / res.length)); 154 | translationY = (float) (r * Math.cos(i * 90 / res.length)); 155 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 156 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 157 | AnimatorSet animSet = new AnimatorSet(); 158 | animSet.playTogether(animatorX, animatorY); 159 | animSet.setDuration(i * intervalTime); 160 | animSet.start(); 161 | } 162 | } 163 | //上边 164 | if (mParams.x > r && mParams.x < statusBarWidth - r && mParams.y == 0) { 165 | for (int i = 0; i < res.length; i++) { 166 | translationX = mParams.x + (float) (r * Math.sin(i * 180 / res.length)); 167 | translationY = (float) (r * Math.cos(i * 180 / res.length)); 168 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 169 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 170 | AnimatorSet animSet = new AnimatorSet(); 171 | animSet.playTogether(animatorX, animatorY); 172 | animSet.setDuration(i * intervalTime); 173 | animSet.start(); 174 | } 175 | } 176 | //右上角 177 | if (mParams.x == statusBarWidth && mParams.y == 0) { 178 | for (int i = 0; i < res.length; i++) { 179 | translationX = statusBarWidth - (float) (r * Math.sin(i * 90 / res.length)); 180 | translationY = (float) (r * Math.cos(i * 90 / res.length)); 181 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 182 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 183 | AnimatorSet animSet = new AnimatorSet(); 184 | animSet.playTogether(animatorX, animatorY); 185 | animSet.setDuration(i * intervalTime); 186 | animSet.start(); 187 | } 188 | } 189 | //左边 190 | if (mParams.x == 0 && mParams.y > r && mParams.y < statusBarHeight - r) { 191 | for (int i = 0; i < res.length; i++) { 192 | translationX = (float) (r * Math.sin(i * 180 / res.length)); 193 | translationY = mParams.y + (float) (r * Math.cos(i * 180 / res.length)); 194 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 195 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 196 | AnimatorSet animSet = new AnimatorSet(); 197 | animSet.playTogether(animatorX, animatorY); 198 | animSet.setDuration(i * intervalTime); 199 | animSet.start(); 200 | } 201 | } 202 | //右边 203 | if (mParams.x == statusBarWidth && mParams.y > r && mParams.y < statusBarHeight - r) { 204 | for (int i = 0; i < res.length; i++) { 205 | translationX = statusBarWidth - (float) (r * Math.sin(i * 180 / res.length)); 206 | translationY = mParams.y + (float) (r * Math.cos(i * 180 / res.length)); 207 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 208 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 209 | AnimatorSet animSet = new AnimatorSet(); 210 | animSet.playTogether(animatorX, animatorY); 211 | animSet.setDuration(i * intervalTime); 212 | animSet.start(); 213 | } 214 | } 215 | //左下角 216 | if (mParams.x == 0 && mParams.y == statusBarHeight) { 217 | for (int i = 0; i < res.length; i++) { 218 | translationX = (float) (r * Math.sin(i * 90 / res.length)); 219 | translationY = mParams.y - (float) (r * Math.cos(i * 90 / res.length)); 220 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 221 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 222 | AnimatorSet animSet = new AnimatorSet(); 223 | animSet.playTogether(animatorX, animatorY); 224 | animSet.setDuration(i * intervalTime); 225 | animSet.start(); 226 | } 227 | } 228 | //下边 229 | if (mParams.x > r && mParams.x < statusBarWidth - r && mParams.y == statusBarHeight) { 230 | for (int i = 0; i < res.length; i++) { 231 | translationX = mParams.x + (float) (r * Math.sin(i * 180 / res.length)); 232 | translationY = statusBarHeight - (float) (r * Math.cos(i * 180 / res.length)); 233 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 234 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 235 | AnimatorSet animSet = new AnimatorSet(); 236 | animSet.playTogether(animatorX, animatorY); 237 | animSet.setDuration(i * intervalTime); 238 | animSet.start(); 239 | } 240 | } 241 | //右下角 242 | if (mParams.x == statusBarWidth && mParams.y == statusBarHeight) { 243 | for (int i = 0; i < res.length; i++) { 244 | translationX = mParams.x - (float) (r * Math.sin(i * 90 / res.length)); 245 | translationY = statusBarHeight - (float) (r * Math.cos(i * 90 / res.length)); 246 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 247 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 248 | AnimatorSet animSet = new AnimatorSet(); 249 | animSet.playTogether(animatorX, animatorY); 250 | animSet.setDuration(i * intervalTime); 251 | animSet.start(); 252 | } 253 | } 254 | // for (int i = 1; i < res.length; i++) { 255 | // translationX = (float) (r * Math.sin(i * angle)); 256 | // translationY = (float) (r * Math.cos(i * angle)); 257 | // animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", 0F, translationX); 258 | // animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, translationY); 259 | // AnimatorSet animSet = new AnimatorSet(); 260 | // animSet.playTogether(animatorX, animatorY); 261 | // animSet.setDuration(i * intervalTime); 262 | // animSet.start(); 263 | // } 264 | isOpen = true; 265 | } 266 | 267 | /** 268 | * 关闭菜单 269 | */ 270 | private void closeAnim() { 271 | ObjectAnimator animatorX = null; 272 | ObjectAnimator animatorY = null; 273 | float translationX;//横坐标偏移距离 274 | float translationY;//纵坐标偏移距离 275 | for (int i = res.length - 1; i > 0; i--) { 276 | translationX = (float) (r * Math.sin(i * angle)); 277 | translationY = (float) (r * Math.cos(i * angle)); 278 | animatorX = ObjectAnimator.ofFloat(imageViewList.get(i), "translationX", translationX, 0F); 279 | animatorY = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", translationY, 0F); 280 | AnimatorSet animSet = new AnimatorSet(); 281 | animSet.playTogether(animatorX, animatorY); 282 | animSet.setDuration((res.length - i) * intervalTime); 283 | animSet.start(); 284 | } 285 | isOpen = false; 286 | } 287 | 288 | /** 289 | * 监听是否按下返回键 290 | */ 291 | public boolean onKeyDown(int keyCode, KeyEvent event) { 292 | //如果按下返回键,执行退出操作 293 | if (keyCode == KeyEvent.KEYCODE_BACK) { 294 | showDialog(); 295 | } 296 | return super.onKeyDown(keyCode, event); 297 | } 298 | 299 | /** 300 | * 退出弹框 301 | */ 302 | private void showDialog() { 303 | new AlertDialog.Builder(getContext()) 304 | .setTitle("退出") 305 | .setMessage("要退出么?") 306 | .setNegativeButton("退出", new DialogInterface.OnClickListener() { 307 | @Override 308 | public void onClick(DialogInterface arg0, int arg1) { 309 | // finish(); 310 | } 311 | }) 312 | .setPositiveButton("返回", new DialogInterface.OnClickListener() { 313 | @Override 314 | public void onClick(DialogInterface arg0, int arg1) { 315 | 316 | } 317 | }).show(); 318 | } 319 | 320 | @Override 321 | public boolean onTouchEvent(MotionEvent event) { 322 | switch (event.getAction()) { 323 | case MotionEvent.ACTION_DOWN: 324 | // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度 325 | xInView = event.getX(); 326 | yInView = event.getY(); 327 | xDownInScreen = event.getRawX(); 328 | yDownInScreen = event.getRawY() - getStatusBarHeight(); 329 | xInScreen = event.getRawX(); 330 | yInScreen = event.getRawY() - getStatusBarHeight(); 331 | break; 332 | case MotionEvent.ACTION_MOVE: 333 | xInScreen = event.getRawX(); 334 | yInScreen = event.getRawY() - getStatusBarHeight(); 335 | // 手指移动的时候更新小悬浮窗的位置 336 | updateViewPosition(); 337 | break; 338 | case MotionEvent.ACTION_UP: 339 | // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。 340 | if (Math.abs(xDownInScreen - xInScreen) < 5 && Math.abs(yDownInScreen - yInScreen) < 5) { 341 | openBigWindow(); 342 | } else { 343 | if ((xInScreen - xInView) > statusBarWidth / 2) { 344 | mParams.x = statusBarWidth; 345 | mParams.y = (int) (yInScreen - yInView); 346 | } else { 347 | mParams.x = 0; 348 | mParams.y = (int) (yInScreen - yInView); 349 | } 350 | windowManager.updateViewLayout(this, mParams); 351 | } 352 | break; 353 | default: 354 | break; 355 | } 356 | return true; 357 | } 358 | 359 | /** 360 | * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。 361 | * 362 | * @param params 小悬浮窗的参数 363 | */ 364 | public void setParams(WindowManager.LayoutParams params) { 365 | mParams = params; 366 | } 367 | 368 | /** 369 | * 更新小悬浮窗在屏幕中的位置。 370 | */ 371 | private void updateViewPosition() { 372 | // if (xInScreen > getStatusBarWidth() / 2) { 373 | // mParams.x = getStatusBarWidth(); 374 | // mParams.y = (int) (yInScreen - yInView); 375 | // } else { 376 | // mParams.x = 0; 377 | // mParams.y = (int) (yInScreen - yInView); 378 | // } 379 | mParams.x = (int) (xInScreen - xInView); 380 | mParams.y = (int) (yInScreen - yInView); 381 | windowManager.updateViewLayout(this, mParams); 382 | } 383 | 384 | /** 385 | * 打开大悬浮窗,同时关闭小悬浮窗。 386 | */ 387 | private void openBigWindow() { 388 | MyWindowManager.createBigWindow(getContext()); 389 | MyWindowManager.removeSmallWindow(getContext()); 390 | } 391 | 392 | /** 393 | * 用于获取状态栏的高度。 394 | * 395 | * @return 返回状态栏高度的像素值。 396 | */ 397 | private int getStatusBarHeight() { 398 | if (statusBarHeight == 0) { 399 | try { 400 | Class c = Class.forName("com.android.internal.R$dimen"); 401 | Object o = c.newInstance(); 402 | Field field = c.getField("status_bar_height"); 403 | int x = (Integer) field.get(o); 404 | statusBarHeight = getResources().getDimensionPixelSize(x); 405 | } catch (Exception e) { 406 | e.printStackTrace(); 407 | } 408 | } 409 | return statusBarHeight; 410 | } 411 | 412 | // private void initFloatingActionsMenu(View view) { 413 | // // 添加 右下角的白色+号按钮 414 | // final ImageView fabIcon = new ImageView(getContext()); 415 | // fabIcon.setImageDrawable(view.getContext().getResources().getDrawable(R.drawable.ic_action_more)); 416 | // final FloatingActionButton fabButton = new FloatingActionButton.Builder(getActivity()) 417 | // .setContentView(fabIcon) 418 | // .setPosition(FloatingActionButton.POSITION_BOTTOM_LEFT) 419 | // .build(); 420 | // 421 | // SubActionButton.Builder rLSubBuilder = new SubActionButton.Builder(getActivity()); 422 | // 423 | // ImageView imageViewQuit = new ImageView(getContext()); 424 | // ImageView imageViewTool = new ImageView(getContext()); 425 | // ImageView imageViewPalette = new ImageView(getContext()); 426 | // ImageView imageViewCamera = new ImageView(getContext()); 427 | // imageViewQuit.setImageDrawable(view.getContext().getResources().getDrawable(R.drawable.icon_fab_take_photo)); 428 | // imageViewTool.setImageDrawable(view.getContext().getResources().getDrawable(R.drawable.icon_fab_new_note)); 429 | // imageViewPalette.setImageDrawable(view.getContext().getResources().getDrawable(R.drawable.icon_fab_markdown)); 430 | // imageViewCamera.setImageDrawable(view.getContext().getResources().getDrawable(R.drawable.icon_fab_finger_paint)); 431 | // 432 | // SubActionButton buttonQuit = rLSubBuilder.setContentView(imageViewQuit).build(); 433 | // SubActionButton buttonPalette = rLSubBuilder.setContentView(imageViewPalette).build(); 434 | // SubActionButton buttonTool = rLSubBuilder.setContentView(imageViewTool).build(); 435 | // SubActionButton buttonCamera = rLSubBuilder.setContentView(imageViewCamera).build(); 436 | // 437 | // // Build the menu with default options: light theme, 90 degrees, 72dp 438 | // // radius. 439 | // // Set 4 default SubActionButtons 440 | // // FloatingActionMenu通过attachTo(fabButton)附着到FloatingActionButton 441 | // final FloatingActionMenu buttonToolMenu = new FloatingActionMenu.Builder(getActivity()) 442 | // .addSubActionView(buttonPalette) 443 | // .addSubActionView(buttonCamera) 444 | // .addSubActionView(buttonTool) 445 | // .addSubActionView(buttonQuit) 446 | // .setStartAngle(0) 447 | // .setEndAngle(-90) 448 | // .attachTo(fabButton) 449 | // .build(); 450 | // 451 | // // Listen menu open and close events to animate the button content view 452 | // buttonToolMenu.setStateChangeListener(new FloatingActionMenu.MenuStateChangeListener() { 453 | // @Override 454 | // public void onMenuOpened(FloatingActionMenu menu) { 455 | // // 增加按钮中的+号图标顺时针旋转45度 456 | // // Rotate the icon of fabButton 45 degrees clockwise 457 | // fabIcon.setRotation(0); 458 | // PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, 45); 459 | // ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(fabIcon, pvhR); 460 | // animation.start(); 461 | // } 462 | // 463 | // @Override 464 | // public void onMenuClosed(FloatingActionMenu menu) { 465 | // // 增加按钮中的+号图标逆时针旋转45度 466 | // // Rotate the icon of fabButton 45 degrees 467 | // // counter-clockwise 468 | // fabIcon.setRotation(45); 469 | // PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, 0); 470 | // ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(fabIcon, pvhR); 471 | // animation.start(); 472 | // } 473 | // }); 474 | // 475 | // RxView.clicks(buttonQuit) 476 | // .throttleFirst(1, TimeUnit.SECONDS) 477 | // .compose(this.bindUntilEvent(FragmentEvent.DESTROY)) 478 | // .subscribe(v -> { 479 | // Voip.getInstance().hangUpCall(callId); 480 | // finishActivity(); 481 | // }); 482 | // 483 | // RxView.clicks(buttonPalette) 484 | // .throttleFirst(1, TimeUnit.SECONDS) 485 | // .compose(this.bindUntilEvent(FragmentEvent.DESTROY)) 486 | // .subscribe(v -> { 487 | // buttonToolMenu.close(true); 488 | //// buttonToolMenu.collapse(); 489 | // dialogPalette.show(); 490 | // }); 491 | // 492 | // RxView.clicks(buttonCamera) 493 | // .throttleFirst(1, TimeUnit.SECONDS) 494 | // .compose(this.bindUntilEvent(FragmentEvent.DESTROY)) 495 | // .subscribe(v -> { 496 | // buttonToolMenu.close(true); 497 | //// buttonToolMenu.collapse(); 498 | // dialogSelectImage.show(); 499 | // }); 500 | // } 501 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/HomeKeyEventBroadCastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | /** 9 | * Created by Administrator on 2017/6/15. 10 | */ 11 | 12 | public class HomeKeyEventBroadCastReceiver extends BroadcastReceiver { 13 | 14 | static final String SYSTEM_REASON = "reason"; 15 | static final String SYSTEM_HOME_KEY = "homekey";//home key 16 | static final String SYSTEM_RECENT_APPS = "recentapps";//long home key 17 | 18 | @Override 19 | public void onReceive(Context context, Intent intent) { 20 | String action = intent.getAction(); 21 | Log.i("KeyEvent", intent.getAction()); 22 | if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { 23 | String reason = intent.getStringExtra(SYSTEM_REASON); 24 | if (reason != null) { 25 | if (reason.equals(SYSTEM_HOME_KEY)) { 26 | // home key处理点 27 | } else if (reason.equals(SYSTEM_RECENT_APPS)) { 28 | // long home key处理点 } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/HomeWatcherReceiver.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.hardware.Camera; 7 | import android.os.Handler; 8 | import android.util.Log; 9 | 10 | /** 11 | * Created by Administrator on 2017/6/15. 12 | */ 13 | 14 | public class HomeWatcherReceiver extends BroadcastReceiver { 15 | private static final String LOG_TAG = "HomeReceiver"; 16 | private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; 17 | private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; 18 | private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; 19 | private static final String SYSTEM_DIALOG_REASON_LOCK = "lock"; 20 | private static final String SYSTEM_DIALOG_REASON_ASSIST = "assist"; 21 | private static final String SYSTEM_DIALOG_REASON_VOICE = "voiceinteraction"; 22 | private long lastTime = 0; 23 | private int num = 1; 24 | private Handler handler; 25 | private boolean status = false; 26 | private FlashLightManager flashLightManager; 27 | 28 | @Override 29 | public void onReceive(final Context context, Intent intent) { 30 | if (flashLightManager == null) { 31 | flashLightManager = new FlashLightManager(context); 32 | flashLightManager.init(); 33 | } 34 | String action = intent.getAction(); 35 | Log.i(LOG_TAG, "onReceive: action: " + action); 36 | if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { 37 | // android.intent.action.CLOSE_SYSTEM_DIALOGS 38 | String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); 39 | Log.i(LOG_TAG, "reason: " + reason); 40 | 41 | if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) { 42 | // 短按Home键 43 | Log.i(LOG_TAG, "homekey" + System.currentTimeMillis() + ":" + lastTime); 44 | //双击 45 | if ((System.currentTimeMillis() - lastTime) < 500) { 46 | num++; 47 | Log.i(LOG_TAG, "homekey点击次数" + num); 48 | } else { 49 | lastTime = System.currentTimeMillis(); 50 | Log.i(LOG_TAG, "homekey开始点击"); 51 | num = 1; 52 | if (handler == null) { 53 | handler = new Handler(); 54 | handler.postDelayed(new Runnable() { 55 | @Override 56 | public void run() { 57 | if (num == 1) { 58 | Log.i(LOG_TAG, "单击"); 59 | MyWindowManager.removeCtrlCenterBigWindow(context); 60 | } else if (num >= 2) { 61 | Log.i(LOG_TAG, "双击"); 62 | MyWindowManager.createCtrlCenterBigWindow(context); 63 | } else { 64 | Log.i(LOG_TAG, "三连击"); 65 | } 66 | lastTime = System.currentTimeMillis(); 67 | num = 1; 68 | handler = null; 69 | } 70 | }, 500); 71 | } 72 | } 73 | } else if (SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) { 74 | // 长按Home键 或者 activity切换键 75 | Log.i(LOG_TAG, "long press home key or activity switch"); 76 | doLongPress(); 77 | } else if (SYSTEM_DIALOG_REASON_LOCK.equals(reason)) { 78 | // 锁屏 79 | Log.i(LOG_TAG, "lock"); 80 | } else if (SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) { 81 | // samsung 长按Home键 82 | Log.i(LOG_TAG, "assist"); 83 | doLongPress(); 84 | } else if (SYSTEM_DIALOG_REASON_VOICE.equals(reason)) { 85 | //google nuex 6p 长按Home键 86 | doLongPress(); 87 | } 88 | } 89 | } 90 | 91 | private void doLongPress() { 92 | if (!status) { 93 | status = true; 94 | flashLightManager.turnOn(); 95 | } else { 96 | status = false; 97 | flashLightManager.turnOff(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.os.Bundle; 8 | import android.support.v7.app.AlertDialog; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.Button; 13 | 14 | public class MainActivity extends AppCompatActivity { 15 | Button startBtn; 16 | Button powerOffBtn; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_main); 22 | startBtn = (Button) findViewById(R.id.vstart); 23 | startBtn.setOnClickListener(new View.OnClickListener() { 24 | @Override 25 | public void onClick(View view) { 26 | MyWindowManager.createCtrlCenterSmallWindow(getApplicationContext()); 27 | } 28 | }); 29 | powerOffBtn = (Button) findViewById(R.id.vpoweroff); 30 | powerOffBtn.setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View v) { 33 | AlarmPowerManager.bcShutdown(getApplicationContext()); 34 | } 35 | }); 36 | findViewById(R.id.vpanel).setOnClickListener(new View.OnClickListener() { 37 | @Override 38 | public void onClick(View v) { 39 | showTips("操作方式与问题", "任意时刻底部上划即弹出面板,屏幕外亦可" + 40 | "你也可以设置左、右边缘轻触启动面板;" + 41 | "如未弹出,请尝试调大触摸感知区域;" + 42 | "确认系统『设置-应用-权限管理』已开启『悬浮窗』等权限", "我知道了", 43 | new DialogInterface.OnClickListener() { 44 | @Override 45 | public void onClick(DialogInterface dialog, int which) { 46 | dialog.dismiss(); 47 | } 48 | }); 49 | } 50 | }); 51 | findViewById(R.id.vprotect).setOnClickListener(new View.OnClickListener() { 52 | @Override 53 | public void onClick(View v) { 54 | showTips("保护应用不被系统停止", "本引用仅有700K左右,内存消耗也极少;" + 55 | "不会后台持续工作,不随意消耗用户电量;不会后台连接网路,不任意消耗用户流量;" + 56 | "未使用黑科技保活,你来决定让它随时服务。", "查看保护方法", new DialogInterface.OnClickListener() { 57 | @Override 58 | public void onClick(DialogInterface dialog, int which) { 59 | dialog.dismiss(); 60 | Intent intent = new Intent(getApplicationContext(), WebActivity.class); 61 | intent.putExtra("url", "https://control.litesuits.com/"); 62 | getApplicationContext().startActivity(intent); 63 | } 64 | }); 65 | } 66 | }); 67 | findViewById(R.id.vsetting).setOnClickListener(new View.OnClickListener() { 68 | @Override 69 | public void onClick(View v) { 70 | getApplicationContext().startActivity(new Intent(getApplicationContext(), SettingActivity.class)); 71 | } 72 | }); 73 | } 74 | 75 | HomeKeyEventBroadCastReceiver homeKeyEventBroadCastReceiver; 76 | 77 | @Override 78 | protected void onStart() { 79 | super.onStart(); 80 | // homeKeyEventBroadCastReceiver = new HomeKeyEventBroadCastReceiver(); 81 | // registerReceiver(homeKeyEventBroadCastReceiver, new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); 82 | } 83 | 84 | @Override 85 | protected void onStop() { 86 | super.onStop(); 87 | } 88 | 89 | private static HomeWatcherReceiver mHomeKeyReceiver = null; 90 | 91 | private static void registerHomeKeyReceiver(Context context) { 92 | Log.i("LOG_TAG", "registerHomeKeyReceiver"); 93 | mHomeKeyReceiver = new HomeWatcherReceiver(); 94 | final IntentFilter homeFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 95 | 96 | context.registerReceiver(mHomeKeyReceiver, homeFilter); 97 | } 98 | 99 | private static void unregisterHomeKeyReceiver(Context context) { 100 | Log.i("LOG_TAG", "unregisterHomeKeyReceiver"); 101 | if (null != mHomeKeyReceiver) { 102 | context.unregisterReceiver(mHomeKeyReceiver); 103 | } 104 | } 105 | 106 | AlertDialog dialog; 107 | 108 | private void showTips(String title, String msg, String ptitle, DialogInterface.OnClickListener listener) { 109 | if (dialog == null) { 110 | dialog = new AlertDialog.Builder(this).create(); 111 | } 112 | dialog.setTitle(title); 113 | dialog.setMessage(msg); 114 | dialog.setButton(AlertDialog.BUTTON_POSITIVE, ptitle, listener); 115 | dialog.show(); 116 | } 117 | 118 | @Override 119 | protected void onResume() { 120 | super.onResume(); 121 | // registerHomeKeyReceiver(this); 122 | } 123 | 124 | @Override 125 | protected void onPause() { 126 | // unregisterHomeKeyReceiver(this); 127 | super.onPause(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/MyWindowManager.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.graphics.PixelFormat; 7 | import android.view.Gravity; 8 | import android.view.WindowManager; 9 | 10 | import java.io.BufferedReader; 11 | import java.io.FileReader; 12 | import java.io.IOException; 13 | import java.util.List; 14 | 15 | import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; 16 | 17 | /** 18 | * Created by Admin on 2017/6/13 0013 09:38. 19 | * Author: kang 20 | * Email: kangsafe@163.com 21 | */ 22 | 23 | public class MyWindowManager { 24 | 25 | /** 26 | * 小悬浮窗View的实例 27 | */ 28 | private static FloatWindowSmallView smallWindow; 29 | /** 30 | * 大悬浮窗View的实例 31 | */ 32 | private static FloatWindowBigView bigWindow; 33 | 34 | /** 35 | * 小控制中心 36 | */ 37 | private static CtrlCenterWindowSmallView ctrlCenterWindowSmallView; 38 | /** 39 | * 大控制中心 40 | */ 41 | private static CtrlCenterWindowBigView ctrlCenterWindowBigView; 42 | 43 | /** 44 | * 小悬浮窗View的参数 45 | */ 46 | private static WindowManager.LayoutParams smallWindowParams; 47 | /** 48 | * 大悬浮窗View的参数 49 | */ 50 | private static WindowManager.LayoutParams bigWindowParams; 51 | 52 | /** 53 | * 小控制中心View的参数 54 | */ 55 | private static WindowManager.LayoutParams ctrlCenterWindowSmallParams; 56 | /** 57 | * 大控制中心View参数 58 | */ 59 | private static WindowManager.LayoutParams ctrlCenterWindowBigParams; 60 | 61 | /** 62 | * 用于控制在屏幕上添加或移除悬浮窗 63 | */ 64 | private static WindowManager mWindowManager; 65 | 66 | /** 67 | * 用于获取手机可用内存 68 | */ 69 | private static ActivityManager mActivityManager; 70 | 71 | /** 72 | * 用来判断服务是否运行. 73 | * 74 | * @param mContext 75 | * @param className 判断的服务名字 76 | * @return true 在运行 false 不在运行 77 | */ 78 | public static boolean isServiceRunning(Context mContext, String className) { 79 | boolean isRunning = false; 80 | ActivityManager activityManager = (ActivityManager) 81 | mContext.getSystemService(Context.ACTIVITY_SERVICE); 82 | List serviceList 83 | = activityManager.getRunningServices(30); 84 | if (!(serviceList.size() > 0)) { 85 | return false; 86 | } 87 | for (int i = 0; i < serviceList.size(); i++) { 88 | if (serviceList.get(i).service.getClassName().equals(className) == true) { 89 | isRunning = true; 90 | break; 91 | } 92 | } 93 | return isRunning; 94 | } 95 | 96 | public static boolean isServiceRunning(Context mContext, Class cla) { 97 | return isServiceRunning(mContext, cla.getName()); 98 | } 99 | 100 | /** 101 | * 启动悬浮服务 102 | */ 103 | public static void startFloatingServiceByConfig() { 104 | try { 105 | // if (SPHelper.getBoolean(SPConst.FLOATING, false)) { 106 | // MyWindowManager.startFloatingService(); 107 | // } 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | 113 | /** 114 | * 启动悬浮服务 115 | */ 116 | public static void startFloatingService() { 117 | try { 118 | // if (!isServiceRunning(MyApplication.getContext(), "com.zd.note.floating.FloatWindowService")) { 119 | // Intent intent = new Intent(MyApplication.getContext(), FloatWindowService.class); 120 | // MyApplication.getContext().startService(intent); 121 | // SPHelper.putBoolean(SPConst.FLOATING, true); 122 | // } 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | 128 | /** 129 | * 停止悬浮服务 130 | */ 131 | public static void stopFloatingService() { 132 | try { 133 | // if (isServiceRunning(MyApplication.getContext(), "com.zd.note.floating.FloatWindowService")) { 134 | // Intent intent = new Intent(MyApplication.getContext(), FloatWindowService.class); 135 | // MyApplication.getContext().stopService(intent); 136 | // } 137 | // SPHelper.putBoolean(SPConst.FLOATING, false); 138 | // removeSmallWindow(MyApplication.getContext()); 139 | } catch (Exception e) { 140 | e.printStackTrace(); 141 | } 142 | } 143 | 144 | /** 145 | * 启动控制中心服务 146 | */ 147 | public static void startCtrlCenterWindowServiceByConfig() { 148 | try { 149 | // if (SPHelper.getBoolean(SPConst.CTRLCENTER, false)) { 150 | // startCtrlCenterWindowService(); 151 | // } 152 | } catch (Exception e) { 153 | e.printStackTrace(); 154 | } 155 | } 156 | 157 | /** 158 | * 启动控制中心服务 159 | */ 160 | public static void startCtrlCenterWindowService() { 161 | try { 162 | // if (!isServiceRunning(MyApplication.getContext(), "com.zd.note.floating.CtrlCenterWindowService")) { 163 | // Intent intent = new Intent(MyApplication.getContext(), CtrlCenterWindowService.class); 164 | // MyApplication.getContext().startService(intent); 165 | // SPHelper.putBoolean(SPConst.CTRLCENTER, true); 166 | // } 167 | } catch (Exception e) { 168 | e.printStackTrace(); 169 | } 170 | } 171 | 172 | /** 173 | * 启动控制中心 174 | */ 175 | public static void stopCtrlCenterWindowService() { 176 | try { 177 | // if (isServiceRunning(MyApplication.getContext(), "com.zd.note.floating.CtrlCenterWindowService")) { 178 | // Intent intent = new Intent(MyApplication.getContext(), CtrlCenterWindowService.class); 179 | // MyApplication.getContext().stopService(intent); 180 | // } 181 | // SPHelper.putBoolean(SPConst.CTRLCENTER, false); 182 | // removeCtrlCenterSmallWindow(MyApplication.getContext()); 183 | } catch (Exception e) { 184 | e.printStackTrace(); 185 | } 186 | } 187 | 188 | /** 189 | * 创建一个小悬浮窗。初始位置为屏幕的右部中间位置。 190 | * 191 | * @param context 必须为应用程序的Context. 192 | */ 193 | public static void createSmallWindow(Context context) { 194 | WindowManager windowManager = getWindowManager(context); 195 | int screenWidth = windowManager.getDefaultDisplay().getWidth(); 196 | int screenHeight = windowManager.getDefaultDisplay().getHeight(); 197 | if (smallWindow == null) { 198 | smallWindow = new FloatWindowSmallView(context); 199 | if (smallWindowParams == null) { 200 | smallWindowParams = new WindowManager.LayoutParams(); 201 | smallWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE; 202 | smallWindowParams.format = PixelFormat.RGBA_8888; 203 | smallWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 204 | | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 205 | smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP; 206 | smallWindowParams.width = FloatWindowSmallView.viewWidth; 207 | smallWindowParams.height = FloatWindowSmallView.viewHeight; 208 | smallWindowParams.x = screenWidth; 209 | smallWindowParams.y = screenHeight / 2; 210 | } 211 | smallWindow.setParams(smallWindowParams); 212 | windowManager.addView(smallWindow, smallWindowParams); 213 | } 214 | } 215 | 216 | /** 217 | * 创建一个小悬浮窗。初始位置为屏幕的右部中间位置。 218 | * 219 | * @param context 必须为应用程序的Context. 220 | */ 221 | public static void createCtrlCenterSmallWindow(Context context) { 222 | WindowManager windowManager = getWindowManager(context); 223 | if (ctrlCenterWindowSmallView == null) { 224 | ctrlCenterWindowSmallView = new CtrlCenterWindowSmallView(context); 225 | if (ctrlCenterWindowSmallParams == null) { 226 | ctrlCenterWindowSmallParams = new WindowManager.LayoutParams(); 227 | ctrlCenterWindowSmallParams.type = WindowManager.LayoutParams.TYPE_PHONE; 228 | ctrlCenterWindowSmallParams.format = PixelFormat.TRANSLUCENT; 229 | ctrlCenterWindowSmallParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 230 | | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 231 | ctrlCenterWindowSmallParams.gravity = Gravity.BOTTOM; 232 | ctrlCenterWindowSmallParams.width = WindowManager.LayoutParams.MATCH_PARENT; 233 | ctrlCenterWindowSmallParams.height = WindowManager.LayoutParams.WRAP_CONTENT; 234 | ctrlCenterWindowSmallParams.x = 0; 235 | ctrlCenterWindowSmallParams.y = 0; 236 | } 237 | ctrlCenterWindowSmallView.setParams(ctrlCenterWindowSmallParams); 238 | windowManager.addView(ctrlCenterWindowSmallView, ctrlCenterWindowSmallParams); 239 | } 240 | } 241 | 242 | /** 243 | * 创建一个小悬浮窗。初始位置为屏幕的右部中间位置。 244 | * 245 | * @param context 必须为应用程序的Context. 246 | */ 247 | public static void createCtrlCenterBigWindow(Context context) { 248 | WindowManager windowManager = getWindowManager(context); 249 | if (ctrlCenterWindowBigView == null) { 250 | ctrlCenterWindowBigView = new CtrlCenterWindowBigView(context); 251 | if (ctrlCenterWindowBigParams == null) { 252 | ctrlCenterWindowBigParams = new WindowManager.LayoutParams(); 253 | ctrlCenterWindowBigParams.type = WindowManager.LayoutParams.TYPE_PHONE; 254 | ctrlCenterWindowBigParams.format = PixelFormat.TRANSLUCENT; 255 | ctrlCenterWindowBigParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|FLAG_WATCH_OUTSIDE_TOUCH; 256 | ctrlCenterWindowBigParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; 257 | ctrlCenterWindowBigParams.width = (int) (windowManager.getDefaultDisplay().getWidth() * 0.95);//WindowManager.LayoutParams.MATCH_PARENT; 258 | ctrlCenterWindowBigParams.height = WindowManager.LayoutParams.WRAP_CONTENT; 259 | ctrlCenterWindowBigParams.x = 0; 260 | ctrlCenterWindowBigParams.y = 50; 261 | } 262 | windowManager.addView(ctrlCenterWindowBigView, ctrlCenterWindowBigParams); 263 | } 264 | } 265 | 266 | 267 | /** 268 | * 将小悬浮窗从屏幕上移除。 269 | * 270 | * @param context 必须为应用程序的Context. 271 | */ 272 | public static void removeSmallWindow(Context context) { 273 | if (smallWindow != null) { 274 | WindowManager windowManager = getWindowManager(context); 275 | windowManager.removeView(smallWindow); 276 | smallWindow = null; 277 | } 278 | } 279 | 280 | /** 281 | * 将小悬浮窗从屏幕上移除。 282 | * 283 | * @param context 必须为应用程序的Context. 284 | */ 285 | public static void removeCtrlCenterSmallWindow(Context context) { 286 | if (ctrlCenterWindowSmallView != null) { 287 | WindowManager windowManager = getWindowManager(context); 288 | windowManager.removeView(ctrlCenterWindowSmallView); 289 | ctrlCenterWindowSmallView = null; 290 | } 291 | } 292 | 293 | /** 294 | * 将小悬浮窗从屏幕上移除。 295 | * 296 | * @param context 必须为应用程序的Context. 297 | */ 298 | public static void removeCtrlCenterBigWindow(Context context) { 299 | if (ctrlCenterWindowBigView != null) { 300 | WindowManager windowManager = getWindowManager(context); 301 | windowManager.removeView(ctrlCenterWindowBigView); 302 | ctrlCenterWindowBigView = null; 303 | } 304 | } 305 | 306 | /** 307 | * 创建一个大悬浮窗。位置为屏幕正中间。 308 | * 309 | * @param context 必须为应用程序的Context. 310 | */ 311 | public static void createBigWindow(Context context) { 312 | WindowManager windowManager = getWindowManager(context); 313 | int screenWidth = windowManager.getDefaultDisplay().getWidth(); 314 | int screenHeight = windowManager.getDefaultDisplay().getHeight(); 315 | if (bigWindow == null) { 316 | bigWindow = new FloatWindowBigView(context); 317 | if (bigWindowParams == null) { 318 | bigWindowParams = new WindowManager.LayoutParams(); 319 | bigWindowParams.x = screenWidth / 2 - FloatWindowBigView.viewWidth / 2; 320 | bigWindowParams.y = screenHeight / 2 - FloatWindowBigView.viewHeight / 2; 321 | bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE; 322 | bigWindowParams.format = PixelFormat.RGBA_8888; 323 | bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP; 324 | bigWindowParams.width = FloatWindowBigView.viewWidth; 325 | bigWindowParams.height = FloatWindowBigView.viewHeight; 326 | } 327 | windowManager.addView(bigWindow, bigWindowParams); 328 | } 329 | } 330 | 331 | /** 332 | * 将大悬浮窗从屏幕上移除。 333 | * 334 | * @param context 必须为应用程序的Context. 335 | */ 336 | public static void removeBigWindow(Context context) { 337 | if (bigWindow != null) { 338 | WindowManager windowManager = getWindowManager(context); 339 | windowManager.removeView(bigWindow); 340 | bigWindow = null; 341 | } 342 | } 343 | 344 | /** 345 | * 更新小悬浮窗的TextView上的数据,显示内存使用的百分比。 346 | * 347 | * @param context 可传入应用程序上下文。 348 | */ 349 | public static void updateUsedPercent(Context context) { 350 | if (smallWindow != null) { 351 | // TextView percentView = (TextView) smallWindow.findViewById(R.id.percent); 352 | // percentView.setText(getUsedPercentValue(context)); 353 | } 354 | } 355 | 356 | /** 357 | * 是否有悬浮窗(包括小悬浮窗和大悬浮窗)显示在屏幕上。 358 | * 359 | * @return 有悬浮窗显示在桌面上返回true,没有的话返回false。 360 | */ 361 | public static boolean isWindowShowing() { 362 | return smallWindow != null || bigWindow != null; 363 | } 364 | 365 | /** 366 | * 如果WindowManager还未创建,则创建一个新的WindowManager返回。否则返回当前已创建的WindowManager。 367 | * 368 | * @param context 必须为应用程序的Context. 369 | * @return WindowManager的实例,用于控制在屏幕上添加或移除悬浮窗。 370 | */ 371 | private static WindowManager getWindowManager(Context context) { 372 | if (mWindowManager == null) { 373 | mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 374 | } 375 | return mWindowManager; 376 | } 377 | 378 | /** 379 | * 如果ActivityManager还未创建,则创建一个新的ActivityManager返回。否则返回当前已创建的ActivityManager。 380 | * 381 | * @param context 可传入应用程序上下文。 382 | * @return ActivityManager的实例,用于获取手机可用内存。 383 | */ 384 | private static ActivityManager getActivityManager(Context context) { 385 | if (mActivityManager == null) { 386 | mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 387 | } 388 | return mActivityManager; 389 | } 390 | 391 | /** 392 | * 计算已使用内存的百分比,并返回。 393 | * 394 | * @param context 可传入应用程序上下文。 395 | * @return 已使用内存的百分比,以字符串形式返回。 396 | */ 397 | public static String getUsedPercentValue(Context context) { 398 | String dir = "/proc/meminfo"; 399 | try { 400 | FileReader fr = new FileReader(dir); 401 | BufferedReader br = new BufferedReader(fr, 2048); 402 | String memoryLine = br.readLine(); 403 | String subMemoryLine = memoryLine.substring(memoryLine.indexOf("MemTotal:")); 404 | br.close(); 405 | long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll("\\D+", "")); 406 | long availableSize = getAvailableMemory(context) / 1024; 407 | int percent = (int) ((totalMemorySize - availableSize) / (float) totalMemorySize * 100); 408 | return percent + "%"; 409 | } catch (IOException e) { 410 | e.printStackTrace(); 411 | } 412 | return "悬浮窗"; 413 | } 414 | 415 | /** 416 | * 获取当前可用内存,返回数据以字节为单位。 417 | * 418 | * @param context 可传入应用程序上下文。 419 | * @return 当前可用内存。 420 | */ 421 | private static long getAvailableMemory(Context context) { 422 | ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); 423 | getActivityManager(context).getMemoryInfo(mi); 424 | return mi.availMem; 425 | } 426 | 427 | } 428 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/SettingActivity.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.content.SharedPreferences; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.widget.CheckBox; 7 | import android.widget.CompoundButton; 8 | import android.widget.SeekBar; 9 | import android.widget.TextView; 10 | 11 | public class SettingActivity extends AppCompatActivity { 12 | SharedPreferences sp; 13 | CheckBox ckb0; 14 | CheckBox ckb1; 15 | CheckBox ckb2; 16 | int num = 0; 17 | SeekBar seekBar; 18 | private String title = "屏幕『底部%1$S设备像素』为感知区域"; 19 | private TextView tv; 20 | int h = 3; 21 | CheckBox seekdefault; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_setting); 27 | sp = getSharedPreferences("setting", MODE_PRIVATE); 28 | 29 | ckb0 = (CheckBox) findViewById(R.id.ckb0); 30 | ckb1 = (CheckBox) findViewById(R.id.ckb1); 31 | ckb2 = (CheckBox) findViewById(R.id.ckb2); 32 | ckb0.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 33 | @Override 34 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 35 | if (b) { 36 | num = 0; 37 | } 38 | setCheckBox(); 39 | } 40 | }); 41 | ckb1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 42 | @Override 43 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 44 | if (b) { 45 | num = 1; 46 | } 47 | setCheckBox(); 48 | } 49 | }); 50 | ckb2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 51 | @Override 52 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 53 | if (b) { 54 | num = 2; 55 | } 56 | setCheckBox(); 57 | } 58 | }); 59 | 60 | tv = (TextView) findViewById(R.id.seektitle); 61 | seekBar = (SeekBar) findViewById(R.id.seek); 62 | seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 63 | @Override 64 | public void onProgressChanged(SeekBar seekBar, int i, boolean b) { 65 | h = i; 66 | setTouchHeight(); 67 | } 68 | 69 | @Override 70 | public void onStartTrackingTouch(SeekBar seekBar) { 71 | 72 | } 73 | 74 | @Override 75 | public void onStopTrackingTouch(SeekBar seekBar) { 76 | 77 | } 78 | }); 79 | seekdefault = (CheckBox) findViewById(R.id.seekdefault); 80 | seekdefault.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 81 | @Override 82 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 83 | if (b) { 84 | h = 0; 85 | setTouchHeight(); 86 | seekBar.setProgress(0); 87 | } 88 | } 89 | }); 90 | h = sp.getInt("height", 0); 91 | setTouchHeight(); 92 | num = sp.getInt("position", 0); 93 | setCheckBox(); 94 | } 95 | 96 | private void setCheckBox() { 97 | if (num == 0) { 98 | ckb0.setChecked(true); 99 | ckb1.setChecked(false); 100 | ckb2.setChecked(false); 101 | } else if (num == 1) { 102 | ckb0.setChecked(false); 103 | ckb1.setChecked(true); 104 | ckb2.setChecked(false); 105 | } else { 106 | ckb0.setChecked(false); 107 | ckb1.setChecked(false); 108 | ckb2.setChecked(true); 109 | } 110 | SharedPreferences.Editor editor = sp.edit(); 111 | editor.putInt("position", num); 112 | editor.commit(); 113 | } 114 | 115 | private void setTouchHeight() { 116 | tv.setText(String.format(title, h + 3)); 117 | SharedPreferences.Editor editor = sp.edit(); 118 | editor.putInt("height", h); 119 | editor.commit(); 120 | if (h != 0) { 121 | seekdefault.setChecked(false); 122 | } else { 123 | seekdefault.setChecked(true); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/com/ks/control/WebActivity.java: -------------------------------------------------------------------------------- 1 | package com.ks.control; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.Window; 6 | import android.webkit.WebSettings; 7 | import android.webkit.WebView; 8 | 9 | public class WebActivity extends AppCompatActivity { 10 | String url; 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | //设置无标题 16 | requestWindowFeature(Window.FEATURE_NO_TITLE); 17 | setContentView(R.layout.activity_web); 18 | url = getIntent().getStringExtra("url"); 19 | WebView web = (WebView) findViewById(R.id.vweb); 20 | WebSettings webSettings = web.getSettings(); 21 | webSettings.setJavaScriptEnabled(true); 22 | web.loadUrl(url); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/res/animator/indicator_animator.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/animator/indicator_animator_reverse.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/animator/scale_with_alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/alertdialog_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/alertdialog_edit_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/floating_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/floating_small_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 36 | 37 | 38 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | 74 | 75 | 76 | 77 | 78 | 83 | 84 | 85 | 86 | 87 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 103 | 104 | 107 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/floating_small_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 16 | 20 | 21 | 28 | 29 | 35 | 36 | 43 | 44 | 45 | 49 | 50 | 53 | 54 | 62 | 63 | 70 | 71 | 72 | 76 | 77 | 81 | 82 | 87 | 88 | 96 | 97 | 106 | 107 | 113 | 114 | 122 | 123 | 132 | 133 | 134 | 141 | 142 | 143 | 147 | 148 | 154 | 155 | 158 | 159 | 167 | 168 | 171 | 172 | 173 | 177 | 178 | 181 | 182 | 190 | 191 | 194 | 195 | 196 | 200 | 201 | 204 | 205 | 213 | 214 | 217 | 218 | 219 | 223 | 224 | 227 | 228 | 236 | 237 | 240 | 241 | 242 | 246 | 247 | 250 | 251 | 259 | 260 | 263 | 264 | 265 | 269 | 270 | 273 | 274 | 282 | 283 | 286 | 287 | 288 | 289 | 293 | 294 |