Unity本地推送消息(二)(完善)

這篇文章是對(duì)Unity通知欄推送的完善......

在上一篇文章Unity通知欄推送消息(一)里簡(jiǎn)單介紹了Unity在IOS平臺(tái)的推送參考,以及安卓平臺(tái)僅推送消息的簡(jiǎn)單實(shí)現(xiàn),這篇文章參考Git上的一個(gè)項(xiàng)目,進(jìn)行了一些完善,在原有的基礎(chǔ)上實(shí)現(xiàn)了通知消息的Action,以及通知的各種屬性的處理.

AndroidStudio里的處理

javaclass截圖
1.直接貼上代碼供參考:

NotificationAction

package xx.xx.xx;
import android.os.Parcel;
import android.os.Parcelable;
public class NotificationAction implements Parcelable {
    public static final Creator CREATOR = new Creator();
    private String identifier;
    private String title;
    private String icon;
    private boolean foreground;
    private String gameObject;
    private String handlerMethod;
    public String getIdentifier() {
        return identifier;
    }
    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getIcon() {
        return icon;
    }
    public void setIcon(String icon) {
        this.icon = icon;
    }
    public boolean isForeground() { return foreground; }
    public void setForeground(boolean foreground) { this.foreground = foreground; }
    public String getGameObject() {
        return gameObject;
    }
    public void setGameObject(String gameObject) {
        this.gameObject = gameObject;
    }
    public String getHandlerMethod() {
        return handlerMethod;
    }
    public void setHandlerMethod(String handlerMethod) {
        this.handlerMethod = handlerMethod;
    }
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(getIdentifier());
        dest.writeString(getTitle());
        dest.writeString(getIcon());
        dest.writeInt(isForeground() ? 1 : 0);
        dest.writeString(getGameObject());
        dest.writeString(getHandlerMethod());
    }
    private static class Creator implements Parcelable.Creator<NotificationAction> {
        @Override
        public NotificationAction createFromParcel(Parcel source) {
            NotificationAction action = new NotificationAction();
            action.setIdentifier(source.readString());
            action.setTitle(source.readString());
            action.setIcon(source.readString());
            action.setForeground(source.readInt() == 1);
            action.setGameObject(source.readString());
            action.setHandlerMethod(source.readString());
            return action;
        }
        @Override
        public NotificationAction[] newArray(int size) {
            return new NotificationAction[size];
        }
    }
}

UnityNotificationActionHandler

package xx.xx.xx;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
public class UnityNotificationActionHandler extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        int id = intent.getIntExtra("id", 0);
        String gameObject = intent.getStringExtra("gameObject");
        String handlerMethod = intent.getStringExtra("handlerMethod");
        String actionId = intent.getStringExtra("actionId");
        boolean foreground = intent.getBooleanExtra("foreground", true);
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel(id);
        if (foreground) {
            Intent launchIntent = new Intent(context, UnityPlayerActivity.class);
            launchIntent.setPackage(null);
            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            context.startActivity(launchIntent);
        }
        UnityPlayer.UnitySendMessage(gameObject, handlerMethod, actionId);
    }
}

UnityNotificationManager 這個(gè)類(lèi)的方法會(huì)在Unity中被調(diào)用到

package xx.xx.xx;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.media.AudioAttributes;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import com.unity3d.player.UnityPlayer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class UnityNotificationManager extends BroadcastReceiver
{
    private static Set<String> channels = new HashSet<>();
    public static void CreateChannel(String identifier, String name, String description, int importance, String soundName, int enableLights, int lightColor, int enableVibration, long[] vibrationPattern, String bundle) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
            return;
        channels.add(identifier);
        NotificationManager nm = (NotificationManager) UnityPlayer.currentActivity.getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel channel = new NotificationChannel(identifier, name, importance);
        channel.setDescription(description);
        if (soundName != null) {
            Resources res = UnityPlayer.currentActivity.getResources();
            int id = res.getIdentifier("raw/" + soundName, null, UnityPlayer.currentActivity.getPackageName());
            AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();
            channel.setSound(Uri.parse("android.resource://" + bundle + "/" + id), audioAttributes);
        }
        channel.enableLights(enableLights == 1);
        channel.setLightColor(lightColor);
        channel.enableVibration(enableVibration == 1);
        if (vibrationPattern == null)
            vibrationPattern = new long[] { 1000L, 1000L };
        channel.setVibrationPattern(vibrationPattern);
        nm.createNotificationChannel(channel);
    }
    private static void createChannelIfNeeded(String identifier, String name, String soundName, boolean enableLights, boolean enableVibration, String bundle) {
        if (channels.contains(identifier))
            return;
        channels.add(identifier);
        CreateChannel(identifier, name, identifier + " notifications", NotificationManager.IMPORTANCE_DEFAULT, soundName, enableLights ? 1 : 0, Color.GREEN, enableVibration ? 1 : 0, null, bundle);
    }
    public static void SetNotification(int id, long delayMs, String title, String message, String ticker, int sound, String soundName, int vibrate,
                                       int lights, String largeIconResource, String smallIconResource, int bgColor, String bundle, String channel,
                                       ArrayList<NotificationAction> actions)
    {
        Activity currentActivity = UnityPlayer.currentActivity;
        AlarmManager am = (AlarmManager)currentActivity.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(currentActivity, UnityNotificationManager.class);
        intent.putExtra("ticker", ticker);
        intent.putExtra("title", title);
        intent.putExtra("message", message);
        intent.putExtra("id", id);
        intent.putExtra("color", bgColor);
        intent.putExtra("sound", sound == 1);
        intent.putExtra("soundName", soundName);
        intent.putExtra("vibrate", vibrate == 1);
        intent.putExtra("lights", lights == 1);
        intent.putExtra("l_icon", largeIconResource);
        intent.putExtra("s_icon", smallIconResource);
        intent.putExtra("bundle", bundle);
        intent.putExtra("channel", channel);
        Bundle b = new Bundle();
        b.putParcelableArrayList("actions", actions);
        intent.putExtra("actionsBundle", b);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
            am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delayMs, PendingIntent.getBroadcast(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT));
        else
            am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delayMs, PendingIntent.getBroadcast(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT));
    }

    public static void SetRepeatingNotification(int id, long delayMs, String title, String message, String ticker, long rep, int sound, String soundName, int vibrate, int lights,
                                                String largeIconResource, String smallIconResource, int bgColor, String bundle, String channel, ArrayList<NotificationAction> actions)
    {
        Activity currentActivity = UnityPlayer.currentActivity;
        AlarmManager am = (AlarmManager)currentActivity.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(currentActivity, UnityNotificationManager.class);
        intent.putExtra("ticker", ticker);
        intent.putExtra("title", title);
        intent.putExtra("message", message);
        intent.putExtra("id", id);
        intent.putExtra("color", bgColor);
        intent.putExtra("sound", sound == 1);
        intent.putExtra("soundName", soundName);
        intent.putExtra("vibrate", vibrate == 1);
        intent.putExtra("lights", lights == 1);
        intent.putExtra("l_icon", largeIconResource);
        intent.putExtra("s_icon", smallIconResource);
        intent.putExtra("bundle", bundle);
        intent.putExtra("channel", channel);
        Bundle b = new Bundle();
        b.putParcelableArrayList("actions", actions);
        intent.putExtra("actionsBundle", b);
        am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delayMs, rep, PendingIntent.getBroadcast(currentActivity, id, intent, 0));
    }

    public void onReceive(Context context, Intent intent)
    {
        NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

        String ticker = intent.getStringExtra("ticker");
        String title = intent.getStringExtra("title");
        String message = intent.getStringExtra("message");
        String s_icon = intent.getStringExtra("s_icon");
        String l_icon = intent.getStringExtra("l_icon");
        int color = intent.getIntExtra("color", 0);
        String bundle = intent.getStringExtra("bundle");
        Boolean sound = intent.getBooleanExtra("sound", false);
        String soundName = intent.getStringExtra("soundName");
        Boolean vibrate = intent.getBooleanExtra("vibrate", false);
        Boolean lights = intent.getBooleanExtra("lights", false);
        int id = intent.getIntExtra("id", 0);
        String channel = intent.getStringExtra("channel");
        Bundle b = intent.getBundleExtra("actionsBundle");
        ArrayList<NotificationAction> actions = b.getParcelableArrayList("actions");
        Resources res = context.getResources();
        Intent notificationIntent = context.getPackageManager().getLaunchIntentForPackage(bundle);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
        stackBuilder.addNextIntent(notificationIntent);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
                notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        if (channel == null)
            channel = "default";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createChannelIfNeeded(channel, title, soundName, lights, vibrate, bundle);
        }
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

        builder.setContentIntent(pendingIntent)
                .setAutoCancel(true)
                .setContentTitle(title)
                .setContentText(message);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            builder.setColor(color);

        if (ticker != null && ticker.length() > 0)
            builder.setTicker(ticker);

        if (s_icon != null && s_icon.length() > 0)
            builder.setSmallIcon(res.getIdentifier(s_icon, "drawable", context.getPackageName()));

        if (l_icon != null && l_icon.length() > 0)
            builder.setLargeIcon(BitmapFactory.decodeResource(res, res.getIdentifier(l_icon, "drawable", context.getPackageName())));

        if (sound) {
            if (soundName != null) {
                int identifier = res.getIdentifier("raw/" + soundName, null, context.getPackageName());
                builder.setSound(Uri.parse("android.resource://" + bundle + "/" + identifier));
            } else
                builder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
        }

        if (vibrate)
            builder.setVibrate(new long[] {
                    1000L, 1000L
            });

        if (lights)
            builder.setLights(Color.GREEN, 3000, 3000);

        if (actions != null) {
            for (int i = 0; i < actions.size(); i++) {
                NotificationAction action = actions.get(i);
                int icon = 0;
                if (action.getIcon() != null && action.getIcon().length() > 0)
                    icon = res.getIdentifier(action.getIcon(), "drawable", context.getPackageName());
                builder.addAction(icon, action.getTitle(), buildActionIntent(action, i));
            }
        }

        Notification notification = builder.build();
        notificationManager.notify(id, notification);
    }

    private static PendingIntent buildActionIntent(NotificationAction action, int id) {
        Activity currentActivity = UnityPlayer.currentActivity;
        Intent intent = new Intent(currentActivity, UnityNotificationActionHandler.class);
        intent.putExtra("id", id);
        intent.putExtra("gameObject", action.getGameObject());
        intent.putExtra("handlerMethod", action.getHandlerMethod());
        intent.putExtra("actionId", action.getIdentifier());
        intent.putExtra("foreground", action.isForeground());
        return PendingIntent.getBroadcast(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }

    public static void CancelPendingNotification(int id)
    {
        Activity currentActivity = UnityPlayer.currentActivity;
        AlarmManager am = (AlarmManager)currentActivity.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(currentActivity, UnityNotificationManager.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        am.cancel(pendingIntent);
    }

    public static void ClearShowingNotifications()
    {
        Activity currentActivity = UnityPlayer.currentActivity;
        NotificationManager nm = (NotificationManager)currentActivity.getSystemService(Context.NOTIFICATION_SERVICE);
        nm.cancelAll();
    }
}
2.res文件夾的處理
res截圖

注意drawable下的.png可自行修改,一般為應(yīng)用圖標(biāo),保證和Unity里的一樣即可

然后生成jar包

Unity里的處理

1.把jar包,AndroidManifest.xml,res文件夾,以及相關(guān)的arr包放到對(duì)應(yīng)的目錄下
Unity目錄截圖
2.修改.xml文件,其他改動(dòng)參考Unity通知欄推送消息(一),最后加上receiver如圖
.xml的修改
3.C#代碼的處理

截取了C#部分核心代碼,以及java的代碼進(jìn)行參照分析

NotificationTest.cs里的方法

public void OneTimeWithActions()
  {
    Debug.Log("OneTimeWithActions");
    LocalNotification.Action action1 = new LocalNotification.Action("background", "In Background", this);
    action1.Foreground = true;
    LocalNotification.Action action2 = new LocalNotification.Action("foreground", "In Foreground", this);
    LocalNotification.SendNotification(1, 5000, "溫馨提示", "多喝熱水", new Color32(0xff, 0x44, 0x44, 255), false, true, true, null, "boing", "default", action1, action2);
  }

LocalNotification.cs里的方法

 public static int SendNotification(int id, long delayMs, string title, string message, Color32 bgColor, bool sound = true, bool vibrate = true, bool lights = true, string bigIcon = "", String soundName = null, string channel = "default", params Action[] actions)
  {
    AndroidJavaClass pluginClass = new AndroidJavaClass(fullClassName);
    if (pluginClass != null)
    {
      pluginClass.CallStatic("SetNotification", id, delayMs, title, message, message,
          sound ? 1 : 0, soundName, vibrate ? 1 : 0, lights ? 1 : 0, bigIcon, "notify_icon_small",
          ToInt(bgColor), bundleIdentifier, channel, PopulateActions(actions));
    }
    return id;
  }
 public static void SetNotification(int id, long delayMs, String title, String message, String ticker, int sound, String soundName, int vibrate,
                                       int lights, String largeIconResource, String smallIconResource, int bgColor, String bundle, String channel,
                                       ArrayList<NotificationAction> actions)
    {
        Activity currentActivity = UnityPlayer.currentActivity;
        AlarmManager am = (AlarmManager)currentActivity.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(currentActivity, UnityNotificationManager.class);
        intent.putExtra("ticker", ticker);
        intent.putExtra("title", title);
        intent.putExtra("message", message);
        intent.putExtra("id", id);
        intent.putExtra("color", bgColor);
        intent.putExtra("sound", sound == 1);
        intent.putExtra("soundName", soundName);
        intent.putExtra("vibrate", vibrate == 1);
        intent.putExtra("lights", lights == 1);
        intent.putExtra("l_icon", largeIconResource);
        intent.putExtra("s_icon", smallIconResource);
        intent.putExtra("bundle", bundle);
        intent.putExtra("channel", channel);
        Bundle b = new Bundle();
        b.putParcelableArrayList("actions", actions);
        intent.putExtra("actionsBundle", b);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
            am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delayMs, PendingIntent.getBroadcast(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT));
        else
            am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delayMs, PendingIntent.getBroadcast(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT));
    }

對(duì)應(yīng)的參數(shù)很清晰,修改對(duì)應(yīng)的值來(lái)實(shí)現(xiàn)不同的效果

按這個(gè)過(guò)程進(jìn)行下來(lái),應(yīng)該是沒(méi)有問(wèn)題,如果出錯(cuò)了:

1.檢查所有用到包名的地方 com.xx.xx 是否一致.
2.AndroidManifest.xml是否配置正確,如果不正確可以通過(guò)導(dǎo)出GoogleProject的方式獲得,覆蓋項(xiàng)目里的然后重新打包.
3.最后附上參考的Git上的項(xiàng)目可直接參考其中的代碼,res,以及AndroidStudio項(xiàng)目里的文件
https://github.com/Agasper/unity-android-notifications

其他的博客參考:
在Unity3D中實(shí)現(xiàn)安卓平臺(tái)的本地通知推送
Unity之a(chǎn)ndroid推送
unity3d 之本地推送

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市坠韩,隨后出現(xiàn)的幾起案子悯恍,更是在濱河造成了極大的恐慌翩瓜,老刑警劉巖朋沮,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異阐虚,居然都是意外死亡烟勋,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)拂盯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)佑女,“玉大人,你說(shuō)我怎么就攤上這事谈竿⊥徘” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵空凸,是天一觀的道長(zhǎng)嚎花。 經(jīng)常有香客問(wèn)我,道長(zhǎng)呀洲,這世上最難降的妖魔是什么紊选? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮两嘴,結(jié)果婚禮上丛楚,老公的妹妹穿的比我還像新娘。我一直安慰自己憔辫,他們只是感情好趣些,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著贰您,像睡著了一般坏平。 火紅的嫁衣襯著肌膚如雪拢操。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天舶替,我揣著相機(jī)與錄音令境,去河邊找鬼。 笑死顾瞪,一個(gè)胖子當(dāng)著我的面吹牛舔庶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播陈醒,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惕橙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了钉跷?” 一聲冷哼從身側(cè)響起弥鹦,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎爷辙,沒(méi)想到半個(gè)月后彬坏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膝晾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年栓始,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片血当。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡混滔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歹颓,到底是詐尸還是另有隱情,我是刑警寧澤油湖,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布巍扛,位于F島的核電站,受9級(jí)特大地震影響乏德,放射性物質(zhì)發(fā)生泄漏撤奸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一喊括、第九天 我趴在偏房一處隱蔽的房頂上張望胧瓜。 院中可真熱鬧,春花似錦郑什、人聲如沸府喳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)钝满。三九已至兜粘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弯蚜,已是汗流浹背孔轴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碎捺,地道東北人路鹰。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像收厨,于是被迫代替她去往敵國(guó)和親晋柱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容