├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── allsettings │ │ └── observer │ │ ├── MainActivity.java │ │ └── ObService.java │ └── res │ ├── drawable │ └── icon.xml │ ├── layout │ ├── item.xml │ └── layout.xml │ ├── mipmap-anydpi-v26 │ └── icon.xml │ ├── mipmap │ └── icon.png │ ├── values-v21 │ └── styles.xml │ └── values │ ├── colors.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | # SettingsObserver 2 | An android app to observe and record the changes of settings (Global, Secure, System) without using ANY PERMISSION! 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 32 7 | 8 | defaultConfig { 9 | applicationId "allsettings.observer" 10 | minSdk 19 11 | 12 | targetSdk 32 13 | versionCode 2 14 | versionName "2" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | } 30 | 31 | dependencies { 32 | 33 | 34 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/allsettings/observer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package allsettings.observer; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.ClipData; 6 | import android.content.ClipboardManager; 7 | import android.content.ComponentName; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.IntentFilter; 11 | import android.content.ServiceConnection; 12 | import android.graphics.Color; 13 | import android.os.Build; 14 | import android.os.Bundle; 15 | import android.os.IBinder; 16 | import android.view.LayoutInflater; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | import android.view.Window; 20 | import android.widget.BaseAdapter; 21 | import android.widget.Button; 22 | import android.widget.CompoundButton; 23 | import android.widget.ListView; 24 | import android.widget.Switch; 25 | import android.widget.TextView; 26 | import android.widget.Toast; 27 | 28 | import java.text.SimpleDateFormat; 29 | import java.util.ArrayList; 30 | import java.util.Calendar; 31 | import java.util.List; 32 | 33 | 34 | public class MainActivity extends Activity { 35 | ListView lv; 36 | private ObService obService; 37 | boolean b = false; 38 | 39 | ServiceConnection conn; 40 | private adapter ap; 41 | static String newdata = null; 42 | 43 | static class adapter extends BaseAdapter { 44 | 45 | private final List history; 46 | private final Context context; 47 | 48 | adapter(Context context) { 49 | this.context = context; 50 | history = new ArrayList<>(); 51 | } 52 | 53 | public void add(String[] data) { 54 | newdata = data[0] + " " + data[1] + " " + data[2]; 55 | data[3] = new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime()); 56 | history.add(0,data); 57 | notifyDataSetChanged(); 58 | } 59 | 60 | 61 | @Override 62 | public int getCount() { 63 | return history.size(); 64 | } 65 | 66 | @Override 67 | public Object getItem(int i) { 68 | return history.get(i); 69 | } 70 | 71 | @Override 72 | public long getItemId(int i) { 73 | return 0; 74 | } 75 | 76 | 77 | @Override 78 | public View getView(int i, View view, ViewGroup viewGroup) { 79 | LayoutInflater inflater = LayoutInflater.from(context); 80 | ViewHolder holder; 81 | if (view == null) { 82 | view = inflater.inflate(R.layout.item, null); 83 | holder = new ViewHolder(); 84 | holder.t1 = view.findViewById(R.id.t1); 85 | holder.t2 = view.findViewById(R.id.t2); 86 | holder.t3 = view.findViewById(R.id.t3); 87 | holder.t4 = view.findViewById(R.id.t4); 88 | holder.b = view.findViewById(R.id.b); 89 | view.setTag(holder); 90 | } else { 91 | holder = (ViewHolder) view.getTag(); 92 | } 93 | String[] change = history.get(i); 94 | holder.t1.setText(String.format("时间:%s", change[3])); 95 | holder.t2.setText(String.format("类别:%s", change[0])); 96 | holder.t3.setText(String.format("名称:%s", change[1])); 97 | holder.t4.setText(String.format("值:%s", change[2])); 98 | holder.b.setOnClickListener(new View.OnClickListener() { 99 | @Override 100 | public void onClick(View view) { 101 | String inline = change[0] + " " + change[1] + " " + change[2]; 102 | ((ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("c", inline)); 103 | Toast.makeText(context, "已复制到剪贴板:\n" + inline, Toast.LENGTH_SHORT).show(); 104 | } 105 | }); 106 | return view; 107 | } 108 | 109 | static class ViewHolder { 110 | TextView t1, t2, t3, t4; 111 | Button b; 112 | } 113 | } 114 | 115 | private BroadcastReceiver mBroadcastReceiver,mBroadcastReceiver1; 116 | 117 | 118 | @Override 119 | protected void onCreate(Bundle savedInstanceState) { 120 | super.onCreate(savedInstanceState); 121 | requestWindowFeature(Window.FEATURE_NO_TITLE); 122 | setContentView(R.layout.layout); 123 | Window window = getWindow(); 124 | 125 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 126 | // 没有这行的话 navigationBarColor 设置不成功 127 | window.setNavigationBarContrastEnforced(false); 128 | } 129 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 130 | 131 | window.setNavigationBarColor(Color.TRANSPARENT); 132 | } 133 | 134 | mBroadcastReceiver = new BroadcastReceiver() { 135 | @Override 136 | public void onReceive(Context context, Intent intent) { 137 | 138 | if (newdata != null) { 139 | Toast.makeText(context, "已复制到剪贴板:\n" + newdata, Toast.LENGTH_SHORT).show(); 140 | ((ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("c", newdata)); 141 | } 142 | } 143 | }; 144 | mBroadcastReceiver1 = new BroadcastReceiver() { 145 | @Override 146 | public void onReceive(Context context, Intent intent) { 147 | finish(); 148 | } 149 | }; 150 | registerReceiver(mBroadcastReceiver, new IntentFilter("intent.COPY")); 151 | registerReceiver(mBroadcastReceiver1, new IntentFilter("intent.EXIT")); 152 | 153 | lv = findViewById(R.id.l); 154 | ap = new adapter(this); 155 | lv.setAdapter(ap); 156 | 157 | conn = new ServiceConnection() { 158 | 159 | @Override 160 | public void onServiceDisconnected(ComponentName name) { 161 | 162 | } 163 | 164 | @Override 165 | public void onServiceConnected(ComponentName name, IBinder service) { 166 | 167 | 168 | //返回一个MsgService对象 169 | obService = ((ObService.MsgBinder) service).getService(); 170 | 171 | //注册回调接口来接收下载进度的变化 172 | obService.setChangeListener(new ObService.ChangeListener() { 173 | 174 | @Override 175 | public void onChange(String[] data) { 176 | runOnUiThread(new Runnable() { 177 | @Override 178 | public void run() { 179 | ap.add(data); 180 | } 181 | }); 182 | } 183 | 184 | 185 | }); 186 | 187 | } 188 | }; 189 | Switch s = findViewById(R.id.s); 190 | s.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 191 | @Override 192 | public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 193 | if (checked) { 194 | if (!b) { 195 | b = true; 196 | bindService(new Intent(MainActivity.this, ObService.class), conn, Context.BIND_AUTO_CREATE); 197 | } 198 | 199 | } else { 200 | if (b) { 201 | b = false; 202 | unbindService(conn); 203 | } 204 | 205 | } 206 | 207 | } 208 | }); 209 | if (!b) { 210 | b = true; 211 | bindService(new Intent(this, ObService.class), conn, Context.BIND_AUTO_CREATE); 212 | } 213 | 214 | 215 | } 216 | 217 | @Override 218 | protected void onDestroy() { 219 | unregisterReceiver(mBroadcastReceiver); 220 | unregisterReceiver(mBroadcastReceiver1); 221 | if (b) 222 | unbindService(conn); 223 | super.onDestroy(); 224 | } 225 | } -------------------------------------------------------------------------------- /app/src/main/java/allsettings/observer/ObService.java: -------------------------------------------------------------------------------- 1 | package allsettings.observer; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationChannel; 5 | import android.app.NotificationManager; 6 | import android.app.PendingIntent; 7 | import android.app.Service; 8 | import android.content.Intent; 9 | import android.database.ContentObserver; 10 | import android.database.Cursor; 11 | import android.graphics.drawable.Icon; 12 | import android.net.Uri; 13 | import android.os.Binder; 14 | import android.os.Build; 15 | import android.os.Handler; 16 | import android.os.IBinder; 17 | import android.widget.Toast; 18 | 19 | import java.util.regex.Pattern; 20 | 21 | public class ObService extends Service { 22 | 23 | 24 | 25 | 26 | @Override 27 | public IBinder onBind(Intent intent) { 28 | return new MsgBinder(); 29 | } 30 | 31 | public class MsgBinder extends Binder { 32 | 33 | public ObService getService() { 34 | return ObService.this; 35 | } 36 | } 37 | 38 | public interface ChangeListener { 39 | void onChange(String[] data); 40 | } 41 | 42 | ChangeListener listener; 43 | 44 | Notification.Builder notification; 45 | NotificationManager systemService; 46 | 47 | 48 | public void setChangeListener(ChangeListener changeListener) { 49 | this.listener = changeListener; 50 | } 51 | 52 | class SettingsValueChangeContentObserver extends ContentObserver { 53 | 54 | public SettingsValueChangeContentObserver() { 55 | super(new Handler()); 56 | } 57 | 58 | @Override 59 | public void onChange(boolean selfChange, Uri uri) { 60 | String uristr = uri.toString().replace("content://settings/", ""); 61 | String namespace = Pattern.compile("/").split(uristr)[0]; 62 | Cursor cursor = getContentResolver().query(uri, null, null, null, null); 63 | while (cursor != null && cursor.moveToNext()) { 64 | String name = cursor.getString(1); 65 | String value = cursor.getString(2); 66 | listener.onChange(new String[]{namespace, name, value, "0"}); 67 | String inline = "类别:" + namespace + ",名称:" + name + ",值:" + value; 68 | Toast.makeText(ObService.this, inline, Toast.LENGTH_SHORT).show(); 69 | notification.setContentText(inline).setContentTitle("监测到以下变动:"); 70 | systemService.notify(1, notification.build()); 71 | 72 | } 73 | assert cursor != null; 74 | cursor.close(); 75 | 76 | } 77 | } 78 | 79 | private SettingsValueChangeContentObserver mContentOb; 80 | 81 | @Override 82 | public void onCreate() { 83 | super.onCreate(); 84 | Toast.makeText(this, "开始监测", Toast.LENGTH_SHORT).show(); 85 | 86 | 87 | mContentOb = new SettingsValueChangeContentObserver(); 88 | getContentResolver().registerContentObserver(Uri.parse("content://settings/"), true, mContentOb); 89 | 90 | 91 | notification = new Notification.Builder(getApplication()).setAutoCancel(true). 92 | setContentText("点击查看历史变动"). 93 | setContentTitle("监测中..."). 94 | setWhen(System.currentTimeMillis()); 95 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 96 | notification. 97 | addAction(android.R.drawable.ic_delete, "复制本次变动", PendingIntent.getBroadcast(this, 0, new Intent("intent.COPY"), PendingIntent.FLAG_IMMUTABLE)). 98 | addAction(android.R.drawable.ic_delete, "退出", PendingIntent.getBroadcast(this, 0, new Intent("intent.EXIT"), PendingIntent.FLAG_IMMUTABLE)). 99 | setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_IMMUTABLE)). 100 | setSmallIcon(Icon.createWithResource(this, R.drawable.icon)); 101 | 102 | } 103 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 104 | NotificationChannel notificationChannel = new NotificationChannel("Ob", "Ob", NotificationManager.IMPORTANCE_DEFAULT); 105 | 106 | notificationChannel.enableLights(false); 107 | 108 | notificationChannel.setShowBadge(true);//是否显示角标 109 | 110 | systemService = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 111 | 112 | systemService.createNotificationChannel(notificationChannel); 113 | notification.setChannelId("Ob"); 114 | } 115 | startForeground(1, notification.build()); 116 | 117 | } 118 | 119 | @Override 120 | public void onDestroy() { 121 | super.onDestroy(); 122 | getContentResolver().unregisterContentObserver(mContentOb); 123 | 124 | Toast.makeText(ObService.this, "停止监测", Toast.LENGTH_SHORT).show(); 125 | } 126 | 127 | @Override 128 | public int onStartCommand(Intent intent, int flags, int startId) { 129 | return super.onStartCommand(intent, flags, startId); 130 | } 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 24 | 25 | 32 | 33 | 40 |