diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b7b46fda..05e1e802 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -95,6 +95,9 @@ + appInfoList = new ArrayList<>(); + private ListView listView; + private String currentType = "user"; + + //消息处理者,创建一个Handler的子类对象,目的是重写Handler的处理消息的方法(handleMessage()) + @SuppressLint("HandlerLeak") + private final Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (msg.what == NOTIFY) { + Toast.makeText(AppListActivity.this, msg.getData().getString("DATA"), Toast.LENGTH_LONG).show(); + } else if (msg.what == APP_LIST) { + AppAdapter adapter = new AppAdapter(AppListActivity.this, R.layout.item_app, appInfoList); + listView.setAdapter(adapter); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate"); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_applist); + } + + @Override + protected void onStart() { + super.onStart(); + Log.d(TAG, "onStart"); + Toast.makeText(AppListActivity.this, "加载应用列表中,请稍候...", Toast.LENGTH_LONG).show(); + + //是否关闭页面提示 + TextView help_tip = findViewById(R.id.help_tip); + help_tip.setVisibility(MyApplication.showHelpTip ? View.VISIBLE : View.GONE); + + //获取应用列表 + getAppList(); + + //切换日志类别 + int typeCheckId = "user".equals(currentType) ? R.id.btnTypeUser : R.id.btnTypeSys; + final RadioGroup radioGroupTypeCheck = findViewById(R.id.radioGroupTypeCheck); + radioGroupTypeCheck.check(typeCheckId); + radioGroupTypeCheck.setOnCheckedChangeListener((group, checkedId) -> { + RadioButton rb = findViewById(checkedId); + currentType = (String) rb.getTag(); + getAppList(); + }); + + listView = findViewById(R.id.list_view_app); + listView.setOnItemClickListener((parent, view, position, id) -> { + AppInfo appInfo = appInfoList.get(position); + Log.d(TAG, "onItemClick: " + appInfo.toString()); + //复制到剪贴板 + ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData mClipData = ClipData.newPlainText("pkgName", appInfo.getPkgName()); + cm.setPrimaryClip(mClipData); + + Toast.makeText(AppListActivity.this, "已复制包名:" + appInfo.getPkgName(), Toast.LENGTH_LONG).show(); + }); + listView.setOnItemLongClickListener((parent, view, position, id) -> { + AppInfo appInfo = appInfoList.get(position); + Log.d(TAG, "onItemClick: " + appInfo.toString()); + //启动应用 + Intent intent; + intent = getPackageManager().getLaunchIntentForPackage(appInfo.getPkgName()); + startActivity(intent); + + return true; + }); + } + + //获取应用列表 + private void getAppList() { + new Thread(() -> { + appInfoList = new ArrayList<>(); + PackageManager pm = getApplication().getPackageManager(); + @SuppressLint("QueryPermissionsNeeded") List packages = pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); + for (PackageInfo packageInfo : packages) { + if ("user".equals(currentType) && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1) { //用户应用 + continue; + } + if ("sys".equals(currentType) && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { //系统应用 + continue; + } + String appName = packageInfo.applicationInfo.loadLabel(pm).toString(); + String packageName = packageInfo.packageName; + Drawable drawable = packageInfo.applicationInfo.loadIcon(pm); + String verName = packageInfo.versionName; + int verCode = packageInfo.versionCode; + AppInfo appInfo = new AppInfo(appName, packageName, drawable, verName, verCode); + appInfoList.add(appInfo); + Log.d(TAG, appInfo.toString()); + } + Message message = new Message(); + message.what = APP_LIST; + message.obj = appInfoList; + handler.sendMessage(message); + }).start(); + } + + @Override + protected void onDestroy() { + Log.d(TAG, "onDestroy"); + super.onDestroy(); + } + + @Override + protected void onResume() { + super.onResume(); + MobclickAgent.onResume(this); + } + + @Override + protected void onPause() { + super.onPause(); + MobclickAgent.onPause(this); + } + +} diff --git a/app/src/main/java/com/idormy/sms/forwarder/CloneActivity.java b/app/src/main/java/com/idormy/sms/forwarder/CloneActivity.java index 49600d3b..f586cd7b 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/CloneActivity.java +++ b/app/src/main/java/com/idormy/sms/forwarder/CloneActivity.java @@ -36,7 +36,7 @@ import okhttp3.Request; import okhttp3.Response; public class CloneActivity extends AppCompatActivity { - private final String TAG = "com.idormy.sms.forwarder.CloneActivity"; + private final String TAG = "CloneActivity"; private Context context; private boolean isRunning = false; private String serverIp; diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/AppAdapter.java b/app/src/main/java/com/idormy/sms/forwarder/adapter/AppAdapter.java new file mode 100644 index 00000000..574353a8 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/AppAdapter.java @@ -0,0 +1,111 @@ +package com.idormy.sms.forwarder.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.idormy.sms.forwarder.R; +import com.idormy.sms.forwarder.model.AppInfo; + +import java.util.List; + +public class AppAdapter extends ArrayAdapter { + private final int resourceId; + private List list; + + // 适配器的构造函数,把要适配的数据传入这里 + public AppAdapter(Context context, int textViewResourceId, List objects) { + super(context, textViewResourceId, objects); + resourceId = textViewResourceId; + list = objects; + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public AppInfo getItem(int position) { + return list.get(position); + } + + @Override + public long getItemId(int position) { + AppInfo item = list.get(position); + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + AppInfo appInfo = getItem(position); //获取当前项的TLog实例 + + // 加个判断,以免ListView每次滚动时都要重新加载布局,以提高运行效率 + View view; + AppAdapter.ViewHolder viewHolder; + if (convertView == null) { + + // 避免ListView每次滚动时都要重新加载布局,以提高运行效率 + view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); + + // 避免每次调用getView()时都要重新获取控件实例 + viewHolder = new AppAdapter.ViewHolder(); + viewHolder.appName = view.findViewById(R.id.appName); + viewHolder.pkgName = view.findViewById(R.id.pkgName); + viewHolder.appIcon = view.findViewById(R.id.appIcon); + viewHolder.verName = view.findViewById(R.id.verName); + viewHolder.verCode = view.findViewById(R.id.verCode); + + // 将ViewHolder存储在View中(即将控件的实例存储在其中) + view.setTag(viewHolder); + } else { + view = convertView; + viewHolder = (AppAdapter.ViewHolder) view.getTag(); + } + + // 获取控件实例,并调用set...方法使其显示出来 + if (appInfo != null) { + viewHolder.appName.setText(appInfo.getAppName()); + viewHolder.pkgName.setText(appInfo.getPkgName()); + viewHolder.appIcon.setBackground(appInfo.getAppIcon()); + viewHolder.verName.setText(appInfo.getVerName()); + viewHolder.verCode.setText(appInfo.getVerCode() + ""); + } + + return view; + } + + public void add(List appModels) { + if (list != null) { + list = appModels; + notifyDataSetChanged(); + } + } + + public void del(List appModels) { + if (list != null) { + list = appModels; + notifyDataSetChanged(); + } + } + + public void update(List appModels) { + if (list != null) { + list = appModels; + notifyDataSetChanged(); + } + } + + // 定义一个内部类,用于对控件的实例进行缓存 + static class ViewHolder { + TextView appName; + TextView pkgName; + ImageView appIcon; + TextView verName; + TextView verCode; + } +} diff --git a/app/src/main/java/com/idormy/sms/forwarder/model/AppInfo.java b/app/src/main/java/com/idormy/sms/forwarder/model/AppInfo.java new file mode 100644 index 00000000..190a7c7a --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/model/AppInfo.java @@ -0,0 +1,47 @@ +package com.idormy.sms.forwarder.model; + +import android.content.Intent; +import android.graphics.drawable.Drawable; + +import lombok.Data; + +@Data +public class AppInfo { + public String pkgName; + public String appName; + public Drawable appIcon; + public Intent appIntent; + public String verName; + public int verCode; + + public AppInfo() { + } + + public AppInfo(String appName) { + this.appName = appName; + } + + public AppInfo(String appName, String pkgName) { + this.appName = appName; + this.pkgName = pkgName; + } + + public AppInfo(String appName, String pkgName, Drawable appIcon, String verName, int verCode) { + this.appName = appName; + this.pkgName = pkgName; + this.appIcon = appIcon; + this.verName = verName; + this.verCode = verCode; + } + + @Override + public String toString() { + return "AppInfo{" + + "appName='" + appName + '\'' + + ", pkgName='" + pkgName + '\'' + + ", appIcon=" + appIcon + + ", verName=" + verName + + ", verCode=" + verCode + + '}'; + } +} diff --git a/app/src/main/java/com/idormy/sms/forwarder/model/vo/LogVo.java b/app/src/main/java/com/idormy/sms/forwarder/model/vo/LogVo.java index b9c6deef..314cfe97 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/model/vo/LogVo.java +++ b/app/src/main/java/com/idormy/sms/forwarder/model/vo/LogVo.java @@ -40,7 +40,7 @@ public class LogVo { } } - return R.drawable.app; + return R.drawable.ic_app; } public int getStatusImageId() { diff --git a/app/src/main/res/layout/activity_applist.xml b/app/src/main/res/layout/activity_applist.xml new file mode 100644 index 00000000..a9f3b0e0 --- /dev/null +++ b/app/src/main/res/layout/activity_applist.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 68460c28..65affe92 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -1,211 +1,217 @@ - - SmsForwarder - Forward to DingTalk/WeCom/FeiShu/Email/Bark/ServerChan/Telegram/Webhook, etc - - OK - Cancel - Delete - Test - Confirm - All - Select - Clone Settings - Settings - About - Rule Setting - Sender Setting - Tips: Pull to refresh; Long press to delete one log entry. - Tips: Tap "NEW FORWARDING RULE" to add a new rule; Long press one to delete; Tap an existing one to edit. - Tips: Tap "NEW SENDER" to add a new sender; Long press one to delete; Tap an existing one to edit. - - Version - Check for updates - Auto startup - Show tips - Open source - Synchronize Mirror - QQ Group - 1st:562854376 - 2nd:31330492 - Join 1st Group - Join 2nd Group - Cache - Purge - Checking… - Yo, you are up to date! - Cache purged - No mobile QQ is installed or not supported by recent version! - - Clear Logs - Delete confirmation - Are you sure to delete this log entry? - The log entry has deleted. - Details - Are you sure to clear all forwarding logs? - Pull to refresh. - Release to refresh. - Refreshing… - - Fwd Rule Settings - Fwd Rule Settings of call - Fwd Rule Settings of app - Delete confirmation - Are you sure to delete this rule entry? - The rule entry has deleted. - Please add a new sender and then choose it. - Please add a sender first. - Select Sender - Rule tester: - New forwarding rule - Test Sim Slot - Test Phone Number - Test Msg Content - Sim Slot - Field - Phone No. - PackageName - SMS - Inform content - Multiple - Type - Is - Contain - Not Contain - Start With - End With - Regex Match - Value - - Invalid sender, abort! - Delete confirmation - Are you sure to delete this rule entry? - The rule entry has deleted. - Select sender type - Not supported - DingTalk Bot Settings - Email Settings - Webhook Settings - WeCom Group Bot Settings - WeCom Settings - Bark Settings - ServerChan·Turbo Settings - Telegram Bot Settings - SMS Settings - FeiShu Bot Settings - 19999999999 - Test content @ - 【JD】code 387481, ihelp.jd.com - Test Group Name - Failed to fwd: - Token cannot be empty - Invalid email settings - Bark-server cannot be empty - Neither ApiToken nor ChatId can be empty - SendKey cannot be empty - WebServer cannot be empty - webHook cannot be empty - The specified member cannot be empty or select @all - Phone number cannot be empty - New Sender - Bark Group Name - Bark-Server, e.g. https://bark.bms.ink/XXXXXXXX/ - Bark-Icon (optional), e.g. http://day.app/assets/images/avatar.jpg - Name - Token e.g. the XXXXXX part of https://oapi.dingtalk.com/robot/send?access_token=XXXXXX - Secret (optional) - At Mobiles e.g. 18888888888,19999999999 - SMTP Host - SMTP Port - SSL - Account - Password/Auth Code - Nickname - Send To - Webhook - Secret (optional) - Corp ID - Agent ID - App Secret - Is at all - Specified Member - Tip:Specify members receive messages, member ID list (multiple recipients with \'|\' space, maximum 1000) - WebHook, e.g. https://qyapi.weixin.qq.com/cgixx?key=xxx - ServerChan\'s SendKey - ApiToken or Custom address - ChatId - Method - WebServer e.g. https://a.b.com/msg?token=xyz - WebParams e.g. payload=%7B%22text%22%3A%22[msg]%22%7D [msg] will be replaced with text message content. \nSupport Json format, for example: {"text":[MSG]}.\n Note: MSG is automatically utF-8 encoded in addition to JSON format - Secret (If empty, sign is not counted) - Sim Slot - Same source - Receive mobile phone numbers separated by, e.g. 15888888888;19999999999 - 仅当无网络时启用 - - Device name - SIM1 Remark - SIM2 Remark - Carrier_Mobile Number - Low power alarm threshold - Value range: 0–100. Left blank or 0 is disabled - Retry interval (seconds) - Retry five times after it fails - Sim slot info attached - Device Name attached - Forward missed calls - Forward app notify - Enable custom templates - Custom templates - Tip:Insert labels as needed;Leave blank to default template - Phone - PackageName - SMS - InformContent - SIM - Time - Device - Restore initial Setting - Battery Optimization - Request Notify Permission - Unknown Number - Incoming telegram - Your phone does not support this setting - Set successfully! - - v1.0 - https://github.com/pppscn/SmsForwarder - https://gitee.com/pp/SmsForwarder - 0KB - SIM1 - SIM2 - 多重匹配规则示例:\n \n 并且 是 手机号 相等 10086\n 或者 是 手机号 相等 10011\n 并且 是 短信内容 包含 欠费\n \n 以上规则表示:收到短信,并且(手机号是10086 或者 手机号是10010),并且 短信内容 包含 欠费 时转发短信\n 注意:每行开始的空格代表层级,太过复杂的多重规则可能导致内存占用很大! - 多重匹配规则示例:\n \n 并且 是 包名 相等 com.tencent.mm\n 或者 是 包名 相等 com.tencent.mm\n 并且 是 通知内容 包含 欠费\n \n 以上规则表示:收到APP通知,并且(包名是com.tencent.mm 或者 包名是com.tencent.mm),并且 通知内容 包含 欠费 时转发通知\n 注意:每行开始的空格代表层级,太过复杂的多重规则可能导致内存占用很大! - POST - GET - - Local IP: - Operation instructions: \n1. Please keep the old and new phones in the same WiFi network, and do not turn on isolation \n2.The old mobile phone directly click "send" button, get "server IP" \n3. After filling in "Server IP" for the new mobile phone, click "Receive" button \n [note], the sender and forwarding rules will be completely covered after the new mobile phone receives! - Send - Stop - I\'m the old phone - Receive - I\'m the new phone - Server IP: - . - Please enter a valid IP address - The server is started successfully - The server has been stopped - This mobile phone is the sender and cannot receive files. - If the Wifi network is not connected, the one-click cloning function cannot be used. - Please enter a valid server IP address - Download Failed - Download Success - Currently on a wireless network - Currently on a mobile network - No network at present - Not connected WIFI - Failed to get IP address - 短 信 - 来 电 - 应 用 - + + SmsForwarder + Forward to DingTalk/WeCom/FeiShu/Email/Bark/ServerChan/Telegram/Webhook, etc + + OK + Cancel + Delete + Test + Confirm + All + Select + Clone Settings + Settings + About + Rule Setting + Sender Setting + App List + Tips: Pull to refresh; Long press to delete one log entry. + Tips: Tap "NEW FORWARDING RULE" to add a new rule; Long press one to delete; Tap an existing one to edit. + Tips: Tap "NEW SENDER" to add a new sender; Long press one to delete; Tap an existing one to edit. + Tips: Tap to copy the package name of APP; Long press one to start and jump to. + + Version + Check for updates + Auto startup + Show tips + Open source + Synchronize Mirror + QQ Group + 1st:562854376 + 2nd:31330492 + Join 1st Group + Join 2nd Group + Cache + Purge + Checking… + Yo, you are up to date! + Cache purged + No mobile QQ is installed or not supported by recent version! + + Clear Logs + Delete confirmation + Are you sure to delete this log entry? + The log entry has deleted. + Details + Are you sure to clear all forwarding logs? + Pull to refresh. + Release to refresh. + Refreshing… + + Fwd Rule Settings + Fwd Rule Settings of call + Fwd Rule Settings of app + Delete confirmation + Are you sure to delete this rule entry? + The rule entry has deleted. + Please add a new sender and then choose it. + Please add a sender first. + Select Sender + Rule tester: + New forwarding rule + Test Sim Slot + Test Phone Number + Test Msg Content + Sim Slot + Field + Phone No. + PackageName + SMS + Inform content + Multiple + Type + Is + Contain + Not Contain + Start With + End With + Regex Match + Value + + Invalid sender, abort! + Delete confirmation + Are you sure to delete this rule entry? + The rule entry has deleted. + Select sender type + Not supported + DingTalk Bot Settings + Email Settings + Webhook Settings + WeCom Group Bot Settings + WeCom Settings + Bark Settings + ServerChan·Turbo Settings + Telegram Bot Settings + SMS Settings + FeiShu Bot Settings + 19999999999 + Test content @ + 【JD】code 387481, ihelp.jd.com + Test Group Name + Failed to fwd: + Token cannot be empty + Invalid email settings + Bark-server cannot be empty + Neither ApiToken nor ChatId can be empty + SendKey cannot be empty + WebServer cannot be empty + webHook cannot be empty + The specified member cannot be empty or select @all + Phone number cannot be empty + New Sender + Bark Group Name + Bark-Server, e.g. https://bark.bms.ink/XXXXXXXX/ + Bark-Icon (optional), e.g. http://day.app/assets/images/avatar.jpg + Name + Token e.g. the XXXXXX part of https://oapi.dingtalk.com/robot/send?access_token=XXXXXX + Secret (optional) + At Mobiles e.g. 18888888888,19999999999 + SMTP Host + SMTP Port + SSL + Account + Password/Auth Code + Nickname + Send To + Webhook + Secret (optional) + Corp ID + Agent ID + App Secret + Is at all + Specified Member + Tip:Specify members receive messages, member ID list (multiple recipients with \'|\' space, maximum 1000) + WebHook, e.g. https://qyapi.weixin.qq.com/cgixx?key=xxx + ServerChan\'s SendKey + ApiToken or Custom address + ChatId + Method + WebServer e.g. https://a.b.com/msg?token=xyz + WebParams e.g. payload=%7B%22text%22%3A%22[msg]%22%7D [msg] will be replaced with text message content. \nSupport Json format, for example: {"text":[MSG]}.\n Note: MSG is automatically utF-8 encoded in addition to JSON format + Secret (If empty, sign is not counted) + Sim Slot + Same source + Receive mobile phone numbers separated by, e.g. 15888888888;19999999999 + 仅当无网络时启用 + + Device name + SIM1 Remark + SIM2 Remark + Carrier_Mobile Number + Low power alarm threshold + Value range: 0–100. Left blank or 0 is disabled + Retry interval (seconds) + Retry five times after it fails + Sim slot info attached + Device Name attached + Forward sms + Forward missed calls + Forward app notify + Enable custom templates + Custom templates + Tip:Insert labels as needed;Leave blank to default template + Phone + PackageName + SMS + InformContent + SIM + Time + Device + Restore initial Setting + Battery Optimization + Request Notify Permission + Unknown Number + Incoming telegram + Your phone does not support this setting + Set successfully! + + v1.0 + https://github.com/pppscn/SmsForwarder + https://gitee.com/pp/SmsForwarder + 0KB + SIM1 + SIM2 + 多重匹配规则示例:\n \n 并且 是 手机号 相等 10086\n 或者 是 手机号 相等 10011\n 并且 是 短信内容 包含 欠费\n \n 以上规则表示:收到短信,并且(手机号是10086 或者 手机号是10010),并且 短信内容 包含 欠费 时转发短信\n 注意:每行开始的空格代表层级,太过复杂的多重规则可能导致内存占用很大! + 多重匹配规则示例:\n \n 并且 是 包名 相等 com.tencent.mm\n 或者 是 包名 相等 com.tencent.mm\n 并且 是 通知内容 包含 欠费\n \n 以上规则表示:收到APP通知,并且(包名是com.tencent.mm 或者 包名是com.tencent.mm),并且 通知内容 包含 欠费 时转发通知\n 注意:每行开始的空格代表层级,太过复杂的多重规则可能导致内存占用很大! + POST + GET + + Local IP: + Operation instructions: \n1. Please keep the old and new phones in the same WiFi network, and do not turn on isolation \n2.The old mobile phone directly click "send" button, get "server IP" \n3. After filling in "Server IP" for the new mobile phone, click "Receive" button \n [note], the sender and forwarding rules will be completely covered after the new mobile phone receives! + Send + Stop + I\'m the old phone + Receive + I\'m the new phone + Server IP: + . + Please enter a valid IP address + The server is started successfully + The server has been stopped + This mobile phone is the sender and cannot receive files. + If the Wifi network is not connected, the one-click cloning function cannot be used. + Please enter a valid server IP address + Download Failed + Download Success + Currently on a wireless network + Currently on a mobile network + No network at present + Not connected WIFI + Failed to get IP address + SMS + Call + App + App Icon + User App + System App + diff --git a/app/src/main/res/values-en/styles.xml b/app/src/main/res/values-en/styles.xml index 4fc13315..05c50f0e 100644 --- a/app/src/main/res/values-en/styles.xml +++ b/app/src/main/res/values-en/styles.xml @@ -6,6 +6,13 @@ @color/colorPrimary @color/colorPrimaryDark @color/colorAccent + @style/OverflowMenu + + +