[
  {
    "path": ".gitignore",
    "content": "#built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\nout/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Windows thumbnail db\nThumbs.db\n\n# OSX files\n.DS_Store\n\n# Log Files\n*.log\n\n# Android Studio\n*.iml\n.gradle/\nbuild/\ncaptures/\n.navigation/\n\n# Intellij IDEA\n.idea/\n\n# Eclipse project files\n.classpath\n.project\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Eclipse Metadata\n.metadata/\n\n# Keystore files\n*.jks\n\n.externalNativeBuild\n\ngoogle-services.json"
  },
  {
    "path": "README.md",
    "content": "# Firebase Cloud Messaging (FCM)\nFCM is just a demo of Android Application which implement Firebase Cloud Messaging. It made for Google I/O Extended 2016 Bangkok \n\n## Prerequisites\n* Supported Android 4.1 or newer\n* Android Studio 3.3.2 or higher\n* google-services.json in app-level folder\n\n## Features\n* Subscribe and Unsubscribe with topics\n* Get token\n* Send message with payload both **notification** and **data**\n* Send message with **a token**, **token group**, **a topic**, **condition**\n* Handle message both **foreground** and **background**\n* Customize notification\n* Support notification channel for Android 0 or newer\n\n## Limitation in the message payload\n* Notification : 2KB limit and a predefined set of user-visible keys\n* Data : 4KB of custom key-value pairs\n\n## Screenshots\n<table width=\"100%\">\n\t<tr>\n\t  <th width=\"33%\"><img src=\"https://cloud.githubusercontent.com/assets/1763410/16547014/9f81c04a-4187-11e6-936c-0d901d91b8e5.png\"></th>\n\t  <th width=\"33%\"><img src=\"https://cloud.githubusercontent.com/assets/1763410/16553886/8f8af5da-41f5-11e6-85c4-cb6937c80bab.png\"></th>\n\t  <th width=\"33%\"><img src=\"https://cloud.githubusercontent.com/assets/1763410/16553891/97fa1d18-41f5-11e6-9f53-e76a61e9b435.png\"></th>\n\t</tr>\n</table>\n\n## Slide\n```FCM\nhttps://docs.google.com/presentation/d/1HPLk1PXaGUjqTlPB6RHsgrTunJNo10kbGZ-JX_7JELw/edit?usp=sharing\n```\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n\tcompileSdkVersion compileAndTargetSdkVersion\n\tbuildToolsVersion '30.0.3'\n\n\tdefaultConfig {\n\t\tapplicationId \"com.example.fcm\"\n\t\tminSdkVersion 21\n\t\ttargetSdkVersion compileAndTargetSdkVersion\n\t\tversionCode 1\n\t\tversionName \"1.0\"\n\t\tresConfigs('en', 'xxxhdpi')\n\t\tndk {\n\t\t\tabiFilters \"x86\", \"x86_64\", \"arm64-v8a\", \"armeabi-v7a\"\n\t\t}\n\t}\n\n\tbuildTypes {\n\t\trelease {\n\t\t\tminifyEnabled true\n\t\t\tshrinkResources true\n\t\t\tproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n\t\t}\n\t\tdebug {\n\t\t\tsplits.abi.enable = false\n\t\t\tsplits.density.enable = false\n\t\t\taaptOptions.cruncherEnabled = false\n\t\t}\n\t}\n\n\tdexOptions {\n\t\tpreDexLibraries true\n\t\tmaxProcessCount 8\n\t}\n}\n\ndependencies {\n\timplementation 'androidx.appcompat:appcompat:1.2.0'\n\timplementation 'com.google.firebase:firebase-analytics:18.0.3'\n\timplementation 'com.google.firebase:firebase-messaging:21.1.0'\n}\n\napply plugin: 'com.google.gms.google-services'"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "-keepattributes Signature\n-keepattributes *Annotation*\n-keepattributes EnclosingMethod\n-keepattributes InnerClasses\n\n-assumenosideeffects class android.util.Log {\n    public static boolean isLoggable(java.lang.String, int);\n    public static int v(...);\n    public static int i(...);\n    public static int w(...);\n    public static int d(...);\n    public static int e(...);\n    public static int wtf(...);\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"com.example.fcm\"\n\t\t  xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\t\t  android:installLocation=\"auto\">\n\n\t<application\n\t\tandroid:allowBackup=\"true\"\n\t\tandroid:hardwareAccelerated=\"true\"\n\t\tandroid:icon=\"@mipmap/ic_launcher\"\n\t\tandroid:label=\"@string/app_name\"\n\t\tandroid:supportsRtl=\"true\"\n\t\tandroid:theme=\"@style/AppTheme\">\n\n\t\t<meta-data\n\t\t\tandroid:name=\"com.google.firebase.messaging.default_notification_channel_id\"\n\t\t\tandroid:value=\"@string/notification_channel_id\" />\n\n\t\t<meta-data\n\t\t\tandroid:name=\"com.google.firebase.messaging.default_notification_icon\"\n\t\t\tandroid:resource=\"@drawable/ic_notification\" />\n\n\t\t<meta-data\n\t\t\tandroid:name=\"com.google.firebase.messaging.default_notification_color\"\n\t\t\tandroid:resource=\"@color/colorAccent\" />\n\n\t\t<service\n\t\t\tandroid:name=\".MyFirebaseMessagingService\"\n\t\t\tandroid:exported=\"false\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"com.google.firebase.MESSAGING_EVENT\"/>\n\t\t\t</intent-filter>\n\t\t</service>\n\n\t\t<activity android:name=\".MainActivity\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.MAIN\"/>\n\t\t\t\t<category android:name=\"android.intent.category.LAUNCHER\"/>\n\t\t\t</intent-filter>\n\t\t</activity>\n\n\t\t<activity android:name=\".SecondActivity\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"OPEN_ACTIVITY_1\"/>\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\"/>\n\t\t\t</intent-filter>\n\t\t</activity>\n\n\t</application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/example/fcm/MainActivity.java",
    "content": "package com.example.fcm;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.google.android.gms.tasks.OnCompleteListener;\nimport com.google.android.gms.tasks.Task;\nimport com.google.firebase.iid.FirebaseInstanceId;\nimport com.google.firebase.iid.InstanceIdResult;\nimport com.google.firebase.messaging.FirebaseMessaging;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.Scanner;\n\nimport static com.example.fcm.R.id.txt;\n\npublic class MainActivity extends AppCompatActivity {\n\tprivate static final String AUTH_KEY = \"key=YOUR-SERVER-KEY\";\n\tprivate TextView mTextView;\n\tprivate String token;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_main);\n\t\tmTextView = findViewById(txt);\n\n\t\tBundle bundle = getIntent().getExtras();\n\t\tif (bundle != null) {\n\t\t\tString tmp = \"\";\n\t\t\tfor (String key : bundle.keySet()) {\n\t\t\t\tObject value = bundle.get(key);\n\t\t\t\ttmp += key + \": \" + value + \"\\n\\n\";\n\t\t\t}\n\t\t\tmTextView.setText(tmp);\n\t\t}\n\n\t\tFirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {\n\t\t\t@Override\n\t\t\tpublic void onComplete(@NonNull Task<InstanceIdResult> task) {\n\t\t\t\tif (!task.isSuccessful()) {\n\t\t\t\t\ttoken = task.getException().getMessage();\n\t\t\t\t\tLog.w(\"FCM TOKEN Failed\", task.getException());\n\t\t\t\t} else {\n\t\t\t\t\ttoken = task.getResult().getToken();\n\t\t\t\t\tLog.i(\"FCM TOKEN\", token);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void showToken(View view) {\n\t\tmTextView.setText(token);\n\t}\n\n\tpublic void subscribe(View view) {\n\t\tFirebaseMessaging.getInstance().subscribeToTopic(\"news\");\n\t\tmTextView.setText(R.string.subscribed);\n\t}\n\n\tpublic void unsubscribe(View view) {\n\t\tFirebaseMessaging.getInstance().unsubscribeFromTopic(\"news\");\n\t\tmTextView.setText(R.string.unsubscribed);\n\t}\n\n\tpublic void sendToken(View view) {\n\t\tsendWithOtherThread(\"token\");\n\t}\n\n\tpublic void sendTokens(View view) {\n\t\tsendWithOtherThread(\"tokens\");\n\t}\n\n\tpublic void sendTopic(View view) {\n\t\tsendWithOtherThread(\"topic\");\n\t}\n\n\tprivate void sendWithOtherThread(final String type) {\n\t\tnew Thread(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tpushNotification(type);\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tprivate void pushNotification(String type) {\n\t\tJSONObject jPayload = new JSONObject();\n\t\tJSONObject jNotification = new JSONObject();\n\t\tJSONObject jData = new JSONObject();\n\t\ttry {\n\t\t\tjNotification.put(\"title\", \"Google I/O 2016\");\n\t\t\tjNotification.put(\"body\", \"Firebase Cloud Messaging (App)\");\n\t\t\tjNotification.put(\"sound\", \"default\");\n\t\t\tjNotification.put(\"badge\", \"1\");\n\t\t\tjNotification.put(\"click_action\", \"OPEN_ACTIVITY_1\");\n\t\t\tjNotification.put(\"icon\", \"ic_notification\");\n\n\t\t\tjData.put(\"picture\", \"https://miro.medium.com/max/1400/1*QyVPcBbT_jENl8TGblk52w.png\");\n\n\t\t\tswitch(type) {\n\t\t\t\tcase \"tokens\":\n\t\t\t\t\tJSONArray ja = new JSONArray();\n\t\t\t\t\tja.put(\"c5pBXXsuCN0:APA91bH8nLMt084KpzMrmSWRS2SnKZudyNjtFVxLRG7VFEFk_RgOm-Q5EQr_oOcLbVcCjFH6vIXIyWhST1jdhR8WMatujccY5uy1TE0hkppW_TSnSBiUsH_tRReutEgsmIMmq8fexTmL\");\n\t\t\t\t\tja.put(token);\n\t\t\t\t\tjPayload.put(\"registration_ids\", ja);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"topic\":\n\t\t\t\t\tjPayload.put(\"to\", \"/topics/news\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"condition\":\n\t\t\t\t\tjPayload.put(\"condition\", \"'sport' in topics || 'news' in topics\");\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tjPayload.put(\"to\", token);\n\t\t\t}\n\n\t\t\tjPayload.put(\"priority\", \"high\");\n\t\t\tjPayload.put(\"notification\", jNotification);\n\t\t\tjPayload.put(\"data\", jData);\n\n\t\t\tURL url = new URL(\"https://fcm.googleapis.com/fcm/send\");\n\t\t\tHttpURLConnection conn = (HttpURLConnection) url.openConnection();\n\t\t\tconn.setRequestMethod(\"POST\");\n\t\t\tconn.setRequestProperty(\"Authorization\", AUTH_KEY);\n\t\t\tconn.setRequestProperty(\"Content-Type\", \"application/json\");\n\t\t\tconn.setDoOutput(true);\n\n\t\t\t// Send FCM message content.\n\t\t\tOutputStream outputStream = conn.getOutputStream();\n\t\t\toutputStream.write(jPayload.toString().getBytes());\n\n\t\t\t// Read FCM response.\n\t\t\tInputStream inputStream = conn.getInputStream();\n\t\t\tfinal String resp = convertStreamToString(inputStream);\n\n\t\t\tHandler h = new Handler(Looper.getMainLooper());\n\t\t\th.post(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tmTextView.setText(resp);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (JSONException | IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tprivate String convertStreamToString(InputStream is) {\n\t\tScanner s = new Scanner(is).useDelimiter(\"\\\\A\");\n\t\treturn s.hasNext() ? s.next().replace(\",\", \",\\n\") : \"\";\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/example/fcm/MyFirebaseMessagingService.java",
    "content": "package com.example.fcm;\n\nimport android.app.Notification;\nimport android.app.NotificationChannel;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Color;\nimport android.media.RingtoneManager;\nimport android.os.Build;\nimport android.os.Bundle;\nimport androidx.core.app.NotificationCompat;\nimport android.util.Log;\n\nimport com.google.firebase.messaging.FirebaseMessagingService;\nimport com.google.firebase.messaging.RemoteMessage;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Map;\n\npublic class MyFirebaseMessagingService extends FirebaseMessagingService {\n\tpublic static final String FCM_PARAM = \"picture\";\n\tprivate static final String CHANNEL_NAME = \"FCM\";\n\tprivate static final String CHANNEL_DESC = \"Firebase Cloud Messaging\";\n\tprivate int numMessages = 0;\n\n\t@Override\n\tpublic void onMessageReceived(RemoteMessage remoteMessage) {\n\t\tsuper.onMessageReceived(remoteMessage);\n\t\tRemoteMessage.Notification notification = remoteMessage.getNotification();\n\t\tMap<String, String> data = remoteMessage.getData();\n\t\tLog.d(\"FROM\", remoteMessage.getFrom());\n\t\tsendNotification(notification, data);\n\t}\n\n\tprivate void sendNotification(RemoteMessage.Notification notification, Map<String, String> data) {\n\t\tBundle bundle = new Bundle();\n\t\tbundle.putString(FCM_PARAM, data.get(FCM_PARAM));\n\n\t\tIntent intent = new Intent(this, SecondActivity.class);\n\t\tintent.putExtras(bundle);\n\n\t\tPendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);\n\n\t\tNotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))\n\t\t\t\t.setContentTitle(notification.getTitle())\n\t\t\t\t.setContentText(notification.getBody())\n\t\t\t\t.setAutoCancel(true)\n\t\t\t\t.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))\n\t\t\t\t//.setSound(Uri.parse(\"android.resource://\" + getPackageName() + \"/\" + R.raw.win))\n\t\t\t\t.setContentIntent(pendingIntent)\n\t\t\t\t.setContentInfo(\"Hello\")\n\t\t\t\t.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))\n\t\t\t\t.setColor(getColor(R.color.colorAccent))\n\t\t\t\t.setLights(Color.RED, 1000, 300)\n\t\t\t\t.setDefaults(Notification.DEFAULT_VIBRATE)\n\t\t\t\t.setNumber(++numMessages)\n\t\t\t\t.setSmallIcon(R.drawable.ic_notification);\n\n\t\ttry {\n\t\t\tString picture = data.get(FCM_PARAM);\n\t\t\tif (picture != null && !\"\".equals(picture)) {\n\t\t\t\tURL url = new URL(picture);\n\t\t\t\tBitmap bigPicture = BitmapFactory.decodeStream(url.openConnection().getInputStream());\n\t\t\t\tnotificationBuilder.setStyle(\n\t\t\t\t\t\tnew NotificationCompat.BigPictureStyle().bigPicture(bigPicture).setSummaryText(notification.getBody())\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\tNotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n\t\t\tNotificationChannel channel = new NotificationChannel(\n\t\t\t\t\tgetString(R.string.notification_channel_id), CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT\n\t\t\t);\n\t\t\tchannel.setDescription(CHANNEL_DESC);\n\t\t\tchannel.setShowBadge(true);\n\t\t\tchannel.canShowBadge();\n\t\t\tchannel.enableLights(true);\n\t\t\tchannel.setLightColor(Color.RED);\n\t\t\tchannel.enableVibration(true);\n\t\t\tchannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500});\n\n\t\t\tassert notificationManager != null;\n\t\t\tnotificationManager.createNotificationChannel(channel);\n\t\t}\n\n\t\tassert notificationManager != null;\n\t\tnotificationManager.notify(0, notificationBuilder.build());\n\t}\n}"
  },
  {
    "path": "app/src/main/java/com/example/fcm/SecondActivity.java",
    "content": "package com.example.fcm;\n\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.widget.TextView;\n\npublic class SecondActivity extends AppCompatActivity {\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_second);\n\n\t\tTextView txt = findViewById(R.id.textView);\n\n\t\tBundle bundle = getIntent().getExtras();\n\t\tif (bundle != null) {\n\t\t\tfor (String key : bundle.keySet()) {\n\t\t\t\tObject value = bundle.get(key);\n\t\t\t\ttxt.append(key + \": \" + value + \"\\n\\n\");\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:paddingBottom=\"@dimen/activity_vertical_margin\"\n\tandroid:paddingLeft=\"@dimen/activity_horizontal_margin\"\n\tandroid:paddingRight=\"@dimen/activity_horizontal_margin\"\n\tandroid:paddingTop=\"@dimen/activity_vertical_margin\"\n\ttools:context=\"com.example.fcm.MainActivity\">\n\n\t<ImageView\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_above=\"@+id/zoneCenter\"\n\t\tandroid:layout_centerHorizontal=\"true\"\n\t\tandroid:layout_margin=\"@dimen/activity_horizontal_margin\"\n\t\tandroid:contentDescription=\"@string/app_name\"\n\t\tandroid:src=\"@mipmap/fcm\"/>\n\n\t<LinearLayout\n\t\tandroid:id=\"@+id/zoneCenter\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_centerInParent=\"true\"\n\t\tandroid:orientation=\"vertical\">\n\n\t\t<Button\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:onClick=\"showToken\"\n\t\t\tandroid:text=\"@string/btn_show_token\"/>\n\n\t\t<LinearLayout\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:orientation=\"horizontal\"\n\t\t\tandroid:paddingBottom=\"@dimen/activity_vertical_margin\"\n\t\t\tandroid:paddingTop=\"@dimen/activity_vertical_margin\">\n\n\t\t\t<Button\n\t\t\t\tstyle=\"@style/CustomButton\"\n\t\t\t\tandroid:text=\"@string/btn_subscribe_news\"\n\t\t\t\tandroid:onClick=\"subscribe\"/>\n\n\t\t\t<Button\n\t\t\t\tstyle=\"@style/CustomButton\"\n\t\t\t\tandroid:text=\"@string/btn_unsubscribe_news\"\n\t\t\t\tandroid:onClick=\"unsubscribe\"/>\n\n\t\t</LinearLayout>\n\n\t\t<LinearLayout\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:orientation=\"horizontal\">\n\n\t\t\t<Button\n\t\t\t\tstyle=\"@style/CustomButton\"\n\t\t\t\tandroid:text=\"@string/btn_send_token\"\n\t\t\t\tandroid:onClick=\"sendToken\"/>\n\n\t\t\t<Button\n\t\t\t\tstyle=\"@style/CustomButton\"\n\t\t\t\tandroid:text=\"@string/btn_send_tokens\"\n\t\t\t\tandroid:onClick=\"sendTokens\"/>\n\n\t\t\t<Button\n\t\t\t\tstyle=\"@style/CustomButton\"\n\t\t\t\tandroid:text=\"@string/btn_send_topic\"\n\t\t\t\tandroid:onClick=\"sendTopic\"/>\n\n\t\t</LinearLayout>\n\n\t</LinearLayout>\n\n\t<TextView\n\t\tandroid:id=\"@+id/txt\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_below=\"@id/zoneCenter\"\n\t\tandroid:paddingBottom=\"@dimen/activity_vertical_margin\"\n\t\tandroid:paddingTop=\"@dimen/activity_vertical_margin\"\n\t\tandroid:gravity=\"center\"\n\t\tandroid:text=\"@string/result\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_second.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n\tandroid:id=\"@+id/activity_second\"\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:tools=\"http://schemas.android.com/tools\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\"\n\tandroid:paddingBottom=\"@dimen/activity_vertical_margin\"\n\tandroid:paddingTop=\"@dimen/activity_vertical_margin\"\n\tandroid:paddingLeft=\"@dimen/activity_horizontal_margin\"\n\tandroid:paddingRight=\"@dimen/activity_horizontal_margin\"\n\ttools:context=\"com.example.fcm.SecondActivity\">\n\n\t<TextView\n\t\tandroid:id=\"@+id/textView\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_centerInParent=\"true\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<color name=\"colorPrimary\">#039be5</color>\n\t<color name=\"colorPrimaryDark\">#0288d1</color>\n\t<color name=\"colorAccent\">#fb8c00</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n\t<!-- Default screen margins, per the Android Design guidelines. -->\n\t<dimen name=\"activity_horizontal_margin\">16dp</dimen>\n\t<dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n\t<string name=\"app_name\">FCM</string>\n\t<string name=\"btn_send_token\">Send Token</string>\n\t<string name=\"btn_send_tokens\">Send Tokens</string>\n\t<string name=\"btn_send_topic\">Send Topic</string>\n\t<string name=\"btn_show_token\">Show Token</string>\n\t<string name=\"btn_subscribe_news\">Subscribe (News)</string>\n\t<string name=\"btn_unsubscribe_news\">Unsubscribe (News)</string>\n\t<string name=\"notification_channel_id\">default</string>\n\t<string name=\"result\">Something will happen here&#8230;</string>\n\t<string name=\"subscribed\">Subscribed to news topic</string>\n\t<string name=\"unsubscribed\">Unsubscribed from news topic</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n\t<style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n\t\t<!-- Customize your theme here. -->\n\t\t<item name=\"colorPrimary\">@color/colorPrimary</item>\n\t\t<item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n\t\t<item name=\"colorAccent\">@color/colorAccent</item>\n\t</style>\n\n\t<style name=\"CustomButton\">\n\t\t<item name=\"android:layout_width\">0dp</item>\n\t\t<item name=\"android:layout_height\">wrap_content</item>\n\t\t<item name=\"android:layout_weight\">1</item>\n\t</style>\n\n</resources>\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n\trepositories {\n\t\tgoogle()\n\t\tjcenter()\n\t\tmavenCentral()\n\t}\n\tdependencies {\n\t\tclasspath 'com.android.tools.build:gradle:4.1.3'\n\t\tclasspath 'com.google.gms:google-services:4.3.5'\n\t}\n}\n\nallprojects {\n\trepositories {\n\t\tgoogle()\n\t\tjcenter()\n\t\tmavenCentral()\n\t}\n}\n\ntask clean(type: Delete) {\n\tdelete rootProject.buildDir\n}\n\next {\n\tcompileAndTargetSdkVersion = 30\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu May 18 21:45:47 ICT 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.0-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.enableJetifier=true\nandroid.useAndroidX=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]