This is the 9th year since the LINE service started on June 23, 2011, but since the number of monthly active users in Japan is about 87 million, most people are familiar with LINE. think. I think that I usually communicate mainly with LINE, but I thought that if I could get data from LINE, which I use frequently, I could use it in various ways, so I wondered if I could somehow get information about the exchange.
I use this method to publish an app called "Pickup Notification Sound" on Google Play that allows LINE notification sounds and ringtones to sound separately!
https://play.google.com/store/apps/details?id=rabbitp.sns.notifisort
Google Play and the Google Play logo are trademarks of Google LLC.
You will receive a message on LINE ↓ Notification is displayed ↓ Get notifications using Android's "Access Notifications" feature ↓ Scrap only the information you need
It is like this. I will show you how to do it later. (If you want to skip the introduction, click [here](# table of contents of scraping method))
It is convenient to use it because there is a reference project.
Click here → https://github.com/oggata/NotificationListenerServiceDemo
If you open Android Studio after downloading, it will look like this:
Since it is a project made in 2015, a warning will be displayed because the Gradle version is old.
Open File menu → Project Structure ..., change the Android Gradle plugin version and Gradle version to whatever you like and click OK.
(I used plugin 4.0.0 / Gradle 6.1.1.)
After clicking OK, if you see a screen like this, change the two parts of "repositories" as follows.
build.gradle
buildscript {
repositories {
google()
jcenter()
}
...abridgement
allprojects {
repositories {
google()
jcenter()
maven {
url "https://jitpack.io"
}
}
}
When the fix is complete, click Retry in the upper left and wait for the sync to finish.
Then the error will appear again, so delete "targetSdkVersion 21" in build.gradle, click the link "Remove minSdkVersion and sync project", and delete the following description.
AndroidManifest.xml
<uses-sdk
android:minSdkVersion="18"
android:targetSdkVersion="18" />
...Other parts omitted
After removing it, open build.gradle again and click "Sync Now" in the upper right corner. This should clear all the errors.
This project is to "display the raw data as an overlay flowing from the top of the screen when a notification comes", but it is a little inconvenient to use for confirmation, so we will improve it.
Place a button in activity_main.xml and add the following program.
MainActivity.java
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Notification permission screen
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
startActivity(intent);
}
});
I thought it would be nice to be able to copy, so this time I will use EditText instead of TextView. Place EditText on the full screen of activity_main.xml and add the following program.
MainActivity.java
public class MainActivity extends Activity {
EditText editText;
String log = "";
...abridgement
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText);
...abridgement
class NotificationReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("notification_msg") + "\n";
final DateFormat df = new SimpleDateFormat("MM/dd HH:mm:ss");
final Date date = new Date(System.currentTimeMillis());
log=df.format(date)+"\n"+msg+"----------------\n"+log;
editText.setText(log);
}
}
}
The new notification will be displayed on top.
Add a button to reset the log and display. Place a button in activity_main.xml and add the following program.
MainActivity.java
Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//reset
log="";
editText.setText("");
}
});
You can also use "sbn.getNotification (). Extras" to get information that is not actually displayed in the notification. You can get the sender name, the full text of the message, etc. from this.
NLService.java
...abridgement
i.putExtra("notification_msg", ""
+ "ID :" + sbn.getId() + "\n"
+ "Txt :" + sbn.getNotification().tickerText + "\n"
+ "Pkg :" + sbn.getPackageName() + "\n"
+ "Tim :" + sbn.getPostTime() + "\n"
+ "extras :" + sbn.getNotification().extras+"\n"
+ "\n");
...abridgement
There are some changes in the layout etc.
For the sake of clarity, we have organized each piece of information you want to scrape.
Part of the program uses the countStringInString function, which counts the number of times a character appears.
//Get the number of character appearances
public static int countStringInString(String target, String searchWord) {
//return (target.length() - target.replaceAll(searchWord, "").length()) / searchWord.length();
Pattern p = Pattern.compile(searchWord);
Matcher m = p.matcher(target);
String result = m.replaceAll("");
int out = target.length() - result.length();
return out;
}
Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
if(sbn.getPackageName().equals("jp.naver.line.android")) {
//Describe here what you want to do when you receive a LINE notification
}
Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).indexOf("880002") != -1 || String.valueOf(sbn.getId()).equals("110000")) {
//For missed calls or notification IDs for free calls → Free calls
} else {
//If not → message
}
Description location: In "onNotificationRemoved" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).equals("110000")) {
//When the free call is rejected or the call end button is pressed and the call ends → Free call
}
You can get the name of the person who sent it. It can be obtained for both individual and group cases. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
//Plastic surgery
String ex=String.valueOf(sbn.getNotification().extras);
ex=ex.substring(8); //Delete 8 characters from the beginning "Bundle[{」
String[] extars = ex.split(",");
int dir = countStringInString(ex, ","); //「,Number of
//Extract the sender name
String Name="";
for (int a = 0; a < dir; a++){
if(extars[a].indexOf("android.title=")==0){
//At the beginning "android.title=Came → Sender name
Name = extars[a].substring(14); //Delete characters from the beginning "android.title=」
break;
}
}
if(Name.indexOf(" - ")!=-1){
//In Android 5/6 series, Name is "[Sender name] - [group name]"become
Name=Name.split(" - ")[0];
}
//Name: Sender name
You can get the name of the group to which the message was sent. Null is returned if it was an individual rather than a group. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
//Plastic surgery
String ex=String.valueOf(sbn.getNotification().extras);
ex=ex.substring(8); //Delete 8 characters from the beginning "Bundle[{」
String[] extars = ex.split(",");
int dir = countStringInString(ex, ","); //「,Number of
//Extract the group name
String GroupName="";
for (int a = 0; a < dir; a++){
if(extars[a].indexOf("android.title=")==0){
//At the beginning "android.title=Came → Sender name
GroupName = extars[a].substring(14); //Delete characters from the beginning "android.title=」
break;
}
}
if(GroupName.indexOf(" - ")!=-1){
//In Android 5/6 series, Name is "[Sender name] - [group name]"become
GroupName=Name.split(" - ")[1];
}else {
GroupName=sbn.getNotification().extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
if(GroupName!=null){
Name=Name.substring(GroupName.length()+2); //At the beginning ":Is given, so delete it
}
}
//GroupName: Group name
You can get the sent message. Even if "..." is displayed in the notification and omitted, the full text can be obtained. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
//Extract the contents
String Text = sbn.getNotification().extras.getString(Notification.EXTRA_TEXT);
//Text: Content
URLs that start with " https://stickershop.line-scdn.net/products/ " and end with ".png " Can be obtained. It is also possible to display the stamp image by HTTP GET based on this URL. (How to display image by HTTP GET: https://akira-watson.com/android/httpurlconnection-get.html) If it is not a stamp, null is returned. By the way, the image size varies depending on the stamp, but it is about 120px or more. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
//Extract the image URL of the stamp
String StampURL="null";
for (int a = 0; a < dir; a++){
if(extars[a].indexOf(" line.sticker.url=")==0){
//"Line" at the beginning.sticker.url=”→ Stamp image URL
StampURL = extars[a].substring(18); //Delete 18 characters from the beginning "line".sticker.url=」
break;
}
}
//StampURL: Stamp image URL
Since it can be obtained as a Bitmap type, it can be displayed in ImageView or saved as PNG. The image size is 108px x 108px. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
Bitmap LargeIcon = drawableToBitmap(sbn.getNotification().getLargeIcon().loadDrawable(NLService.this));
...abridgement
//Convert Drawable type to Bitmap type
public static Bitmap drawableToBitmap (Drawable drawable) {
Bitmap bitmap = null;
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if(bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
An identifier that is fixed for each friend or group. You can use it to determine if you have friends or groups with the same name. Perhaps LINE's "Create Talk Shortcut" feature is a URL scheme that uses this ID, but I couldn't find it when I searched ...
NLService.java
//Plastic surgery
String ex=String.valueOf(sbn.getNotification().extras);
ex=ex.substring(8); //Delete 8 characters from the beginning "Bundle[{」
String[] extars = ex.split(",");
int dir = countStringInString(ex, ","); //「,Number of
//Extract chat ID
String ChatID="";
for (int a = 0; a < dir; a++){
if(extars[a].indexOf("line.chat.id=")==0){
//"Line" at the beginning.chat.id=Came → Chat ID
ChatID = extars[a].substring(13); //Delete 13 characters from the beginning "line".chat.id=」
break;
}
}
//ChatID: Chat ID
Called when you open the LINE talk screen and mark it as read, or delete the message notification. Description location: In "onNotificationRemoved" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).indexOf("880000") != -1) { //Either 15880000 or 16880000?
//Marked as read or when the notification is deleted
}
The process is called in the following flow.
Called when you make a call. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).indexOf("110000") != -1 && Text.indexOf("Outgoing") != -1) { //Either 15880002 or 1688002?
//Start calling
}
Called when you receive a call. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).indexOf("110000") != -1 && Text.indexOf("Incoming call") != -1) { //Either 15880002 or 1688002?
//Incoming call
}
Called when there is an incoming call and the other party hangs up (presses the reject button or does not answer). Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).indexOf("880002") != -1 && Name.equals("LINE missed call")) { //Either 15880002 or 1688002?
//Missed call
}
It is called when the call is started. Description location: In "onNotificationPosted" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).indexOf("110000") != -1 && Text.indexOf("talking") != -1) { //Either 15880002 or 1688002?
//Start a call
}
Called whenever the call ends in some way. Description location: In "onNotificationRemoved" of "NLService.java"
NLService.java
if (String.valueOf(sbn.getId()).equals("110000")) {
//End call
}
This is a method that I found by actually receiving LINE notifications and verifying how to scrape successfully. Please note that there is a possibility that it will not be usable in the future depending on the specification change of LINE. (Actually, there was a major specification change in 2018, and some changes were made in 2020.)
Also, under Android 7.0 (?), There seems to be a problem that "onNotificationPosted" is not called while trying. If you want to use it for verification, please use Android 7.0 or above. We also recommend setting minSdkVersion to 24 (Android 7.0) or higher.
Recommended Posts