{"id":3644,"date":"2025-04-14T11:29:20","date_gmt":"2025-04-14T11:29:20","guid":{"rendered":"https:\/\/alsaeeddev.com\/?p=3644"},"modified":"2025-04-14T12:07:42","modified_gmt":"2025-04-14T12:07:42","slug":"android-image-compressor-app-with-image-picker","status":"publish","type":"post","link":"https:\/\/alsaeeddev.com\/shop\/android-image-compressor-app-with-image-picker\/","title":{"rendered":"\ud83d\udcf1 Android Image Compressor App with Image Picker and Storage"},"content":{"rendered":"<p>If you&#8217;re looking to build an Android Image Compressor App with Image Picker that lets users select, compress, and save images directly to their device, you&#8217;re in the right place. In this tutorial, we\u2019ll walk you through how to create an Android image compression tool using Java, <code>ActivityResultLauncher<\/code>, <code>SeekBar<\/code>, and a multithreaded approach for performance.<\/p>\n<h3>\ud83d\udd27 What This App Does<\/h3>\n<ul>\n<li>Select images using the modern Image Picker API<\/li>\n<li>Display original and compressed images<\/li>\n<li>Adjust compression quality via SeekBar<\/li>\n<li>Save compressed images to external storage<\/li>\n<li>Optimize image processing using ExecutorService<\/li>\n<\/ul>\n<h3>\ud83d\ude80 Step-by-Step Breakdown<\/h3>\n<h4>1. Image Selection Using ActivityResultLauncher<\/h4>\n<p>The app uses <code>ActivityResultContracts.PickVisualMedia()<\/code> to allow users to select an image from their gallery. Once an image is picked, it&#8217;s displayed in the original image view using:<\/p>\n<pre><code class=\"language-java\"> pickMultipleMedia.launch(new PickVisualMediaRequest.Builder()\r\n    .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE)\r\n    .build());<\/code><\/pre>\n<h4>2. Live Compression Quality Adjustment<\/h4>\n<p>A SeekBar allows users to set the compression level in real-time. The <code>compressionQuality<\/code> variable updates with each user interaction, and the value is shown dynamically using a TextView.<\/p>\n<pre><code class=\"language-java\"> tvSeekValue.setText(\"Compression: \" + progress + \"%\");<\/code><\/pre>\n<h4>3. Image Compression in Background Threads<\/h4>\n<p>To keep the UI responsive, the app uses <code>ExecutorService<\/code> to handle compression on a background thread. This ensures the main thread isn&#8217;t blocked while processing images.<\/p>\n<pre><code class=\"language-java\"> Bitmap compressedBitmap = compressImage(imageModel.getOriginalImage(), compressionQuality);<\/code><\/pre>\n<h4>4. Saving Compressed Images<\/h4>\n<p>Once an image is compressed, users can tap a button to save it into the <code>Pictures\/CompressedImages<\/code> folder. The file is written using <code>FileOutputStream<\/code>, and then registered in the device gallery.<\/p>\n<pre><code class=\"language-java\"> addImageToGallery(file);<\/code><\/pre>\n<h4>5. Displaying the Result<\/h4>\n<p>The app shows both the original and compressed image side by side, allowing users to visually compare them. A progress bar is used to indicate when compression is happening in the background.<\/p>\n<h3>\ud83e\udde0 Key Concepts Used<\/h3>\n<ul>\n<li>Bitmap Compression with <code>Bitmap.compress()<\/code><\/li>\n<li>Multithreading with <code>Executors.newFixedThreadPool()<\/code><\/li>\n<li>Modern Media Picker with <code>ActivityResultContracts<\/code><\/li>\n<li>Storage Access with <code>Environment.getExternalStoragePublicDirectory()<\/code><\/li>\n<li>Gallery Update via <code>MediaStore<\/code><\/li>\n<\/ul>\n<h3>\ud83d\udee1\ufe0f Final Touch: Cleanup<\/h3>\n<p>Always remember to shut down the ExecutorService when the activity is destroyed to prevent memory leaks:<\/p>\n<pre><code class=\"language-java\"> @Override\r\nprotected void onDestroy() {\r\n    super.onDestroy();\r\n    executorService.shutdown();\r\n}<\/code><\/pre>\n<h3>Complete Code Here<\/h3>\n<hr \/>\n<h4>1. MainActivity.java<\/h4>\n<pre><code class=\"language-java\">\r\npublic class MainActivity extends AppCompatActivity {\r\n\r\n\r\n    private Button btnImagesSelect, btnSaveImages, btnCompressImages;\r\n    private TextView tvSeekValue;\r\n    private SeekBar compressionSeekBar;\r\n    private int compressionQuality = 100;  \/\/ Default to 100%\r\n    ImageModel imageModel;\r\n    private ImageView originalImageView, compressedImageView;\r\n    private ProgressBar progressBar;\r\n\r\n    private final ExecutorService executorService = Executors.newFixedThreadPool(2);\r\n\r\n    ActivityResultLauncher pickMultipleMedia =\r\n            registerForActivityResult(new ActivityResultContracts.PickVisualMedia(), uri -&gt; {\r\n\r\n\r\n                if (uri != null) {\r\n                    \/\/ Handle the selected media URI (image or video)\r\n                    Log.d(\"Selected URI\", uri.toString());\r\n                    setImageInImageView(uri);\r\n                } else {\r\n                    Log.d(\"PickMedia\", \"No media selected\");\r\n                }\r\n\r\n\r\n            });\r\n\r\n    @Override\r\n    protected void onCreate(Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n        setContentView(R.layout.activity_main);\r\n\r\n\r\n        originalImageView = findViewById(R.id.originalImageView);\r\n        compressedImageView = findViewById(R.id.compressedImageView);\r\n        btnImagesSelect = findViewById(R.id.selectImagesButton);\r\n        btnSaveImages = findViewById(R.id.saveImagesButton);\r\n        btnCompressImages = findViewById(R.id.compressImages);\r\n        compressionSeekBar = findViewById(R.id.compressionSeekBar);\r\n        tvSeekValue = findViewById(R.id.tvSeekValue);\r\n        progressBar = findViewById(R.id.progressBar);\r\n\r\n\r\n        btnImagesSelect.setOnClickListener(view -&gt; openImagePicker());\r\n        btnSaveImages.setOnClickListener(view -&gt; saveCompressedImages());\r\n\r\n        btnCompressImages.setOnClickListener(v -&gt; compressImagesInBackground());\r\n\r\n        compressionSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\r\n            @Override\r\n            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\r\n                compressionQuality = progress;\r\n                tvSeekValue.setText(\"Compression: \" + progress + \"%\");\r\n            }\r\n\r\n            @Override\r\n            public void onStartTrackingTouch(SeekBar seekBar) {\r\n            }\r\n\r\n            @Override\r\n            public void onStopTrackingTouch(SeekBar seekBar) {\r\n            }\r\n        });\r\n    }\r\n\r\n\r\n    \/\/ open image picker to pick the image\r\n    private void openImagePicker() {\r\n        pickMultipleMedia.launch(new PickVisualMediaRequest.Builder()\r\n                .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE)\r\n                .build());\r\n    }\r\n\r\n\r\n\r\n    \/\/ set the selected image in the original image view\r\n    private void setImageInImageView(Uri uri) {\r\n        executorService.execute(() -&gt; {\r\n            try {\r\n                InputStream inputStream = getContentResolver().openInputStream(uri);\r\n                Bitmap originalBitmap = BitmapFactory.decodeStream(inputStream);\r\n                imageModel = new ImageModel(originalBitmap, uri);\r\n\r\n\r\n                runOnUiThread(() -&gt; {\r\n                    originalImageView.setImageBitmap(originalBitmap);\r\n                    compressedImageView.setImageBitmap(null);\r\n\r\n                });\r\n            } catch (IOException e) {\r\n                e.printStackTrace();\r\n            }\r\n        });\r\n    }\r\n\r\n\r\n\r\n    \/\/ compressed image in the background\r\n    private void compressImagesInBackground() {\r\n        progressBar.setVisibility(View.VISIBLE);\r\n        executorService.execute(() -&gt; {\r\n\r\n            Bitmap compressedBitmap = compressImage(imageModel.getOriginalImage(), compressionQuality);\r\n            imageModel.setCompressedImage(compressedBitmap);\r\n\r\n            runOnUiThread(() -&gt; {\r\n                compressedImageView.setImageBitmap(compressedBitmap);\r\n                progressBar.setVisibility(View.GONE);\r\n            });\r\n\r\n        });\r\n    }\r\n\r\n\r\n    \/\/compress image method, which is calling in the compressImagesInBackground method\r\n    private Bitmap compressImage(Bitmap original, int quality) {\r\n        ByteArrayOutputStream stream = new ByteArrayOutputStream();\r\n        original.compress(Bitmap.CompressFormat.JPEG, quality, stream);\r\n        byte[] byteArray = stream.toByteArray();\r\n        return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);\r\n    }\r\n\r\n\r\n\r\n    \/\/ save the compressed image in the phone storage\r\n    private void saveCompressedImages() {\r\n        executorService.execute(() -&gt; {\r\n            File directory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), \"CompressedImages\");\r\n            if (!directory.exists()) {\r\n                directory.mkdirs();\r\n            }\r\n\r\n            File file = new File(directory, \"compressed_\" + System.currentTimeMillis() + \".jpg\");\r\n            try (FileOutputStream fos = new FileOutputStream(file)) {\r\n                imageModel.getCompressedImage().compress(Bitmap.CompressFormat.JPEG, 100, fos);\r\n                fos.flush();\r\n                addImageToGallery(file);\r\n            } catch (IOException e) {\r\n                e.printStackTrace();\r\n            }\r\n\r\n            \/\/ Show toast with full image path\r\n            runOnUiThread(() -&gt; Toast.makeText(\r\n                    this,\r\n                    \"Image saved at:\\n\" + file.getAbsolutePath(),\r\n                    Toast.LENGTH_LONG\r\n            ).show());\r\n        });\r\n    }\r\n\r\n\r\n    \/\/show the image in the gallery\r\n    private void addImageToGallery(File file) {\r\n        ContentValues values = new ContentValues();\r\n        values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());\r\n        values.put(MediaStore.Images.Media.MIME_TYPE, \"image\/jpeg\");\r\n        getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);\r\n    }\r\n\r\n    @Override\r\n    protected void onDestroy() {\r\n        super.onDestroy();\r\n        executorService.shutdown();\r\n    }\r\n}\r\n<\/code><\/pre>\n<h3>\ud83d\udce6 2. ImageModel.java \u2013 Custom Model Class for Handling Images<\/h3>\n<p><strong>\ud83d\udca1 Note:<\/strong> This is a reusable model class used to store and manage both the <strong>original<\/strong> and <strong>compressed<\/strong> image Bitmaps, along with the <strong>image URI<\/strong>. While it&#8217;s helpful for clean code and scalability, you can also work without a model class if you prefer a simpler implementation.<\/p>\n<pre><code class=\"language-java\">\r\npublic class ImageModel {\r\n    private Bitmap originalImage;\r\n    private Bitmap compressedImage;\r\n    private Uri imageUri;\r\n\r\n    public ImageModel(Bitmap originalImage, Uri imageUri) {\r\n        this.originalImage = originalImage;\r\n        this.imageUri = imageUri;\r\n        this.compressedImage = originalImage;\r\n    }\r\n\r\n    public Bitmap getOriginalImage() {\r\n        return originalImage;\r\n    }\r\n\r\n    public Bitmap getCompressedImage() {\r\n        return compressedImage;\r\n    }\r\n\r\n    public void setCompressedImage(Bitmap compressedImage) {\r\n        this.compressedImage = compressedImage;\r\n    }\r\n\r\n    public Uri getImageUri() {\r\n        return imageUri;\r\n    }\r\n}\r\n<\/code><\/pre>\n<h4>3. activity_main.xml<\/h4>\n<pre><code class=\"language-java\"> &lt;ScrollView xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\r\n    xmlns:app=\"http:\/\/schemas.android.com\/apk\/res-auto\"\r\n    xmlns:tools=\"http:\/\/schemas.android.com\/tools\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"\r\n    android:fillViewport=\"true\"\r\n    android:orientation=\"vertical\"\r\n    android:padding=\"16dp\"\r\n    tools:context=\".MainActivity\"&gt;\r\n\r\n    &lt;androidx.constraintlayout.widget.ConstraintLayout\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"&gt;\r\n\r\n        &lt;TextView\r\n            android:id=\"@+id\/tvOriginal\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_marginTop=\"8dp\"\r\n            android:gravity=\"center\"\r\n            android:text=\"Original Image\"\r\n            android:textColor=\"@color\/black\"\r\n            android:textSize=\"16sp\"\r\n            android:textStyle=\"bold\"\r\n            app:layout_constraintBottom_toTopOf=\"@id\/cvOriginal\"\r\n            app:layout_constraintEnd_toEndOf=\"@id\/cvOriginal\"\r\n            app:layout_constraintStart_toStartOf=\"@id\/cvOriginal\"\r\n            app:layout_constraintTop_toTopOf=\"parent\" \/&gt;\r\n\r\n        &lt;TextView\r\n            android:id=\"@+id\/tvCompressed\"\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_marginTop=\"8dp\"\r\n            android:gravity=\"center\"\r\n            android:text=\"Compressed Image\"\r\n            android:textColor=\"@color\/black\"\r\n            android:textSize=\"16sp\"\r\n            android:textStyle=\"bold\"\r\n            app:layout_constraintBottom_toTopOf=\"@id\/cvCompressed\"\r\n            app:layout_constraintEnd_toEndOf=\"@id\/cvCompressed\"\r\n            app:layout_constraintStart_toStartOf=\"@id\/cvCompressed\"\r\n            app:layout_constraintTop_toTopOf=\"parent\" \/&gt;\r\n\r\n        &lt;androidx.cardview.widget.CardView\r\n            android:id=\"@+id\/cvOriginal\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_height=\"300dp\"\r\n            app:cardCornerRadius=\"8dp\"\r\n            app:layout_constraintBottom_toTopOf=\"@+id\/compressionSeekBar\"\r\n            app:layout_constraintEnd_toStartOf=\"@+id\/cvCompressed\"\r\n            app:layout_constraintHorizontal_bias=\"0.5\"\r\n            app:layout_constraintStart_toStartOf=\"parent\"\r\n            app:layout_constraintTop_toTopOf=\"parent\"&gt;\r\n\r\n            &lt;ImageView\r\n                android:id=\"@+id\/originalImageView\"\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"match_parent\"\r\n                android:scaleType=\"centerCrop\" \/&gt;\r\n\r\n        &lt;\/androidx.cardview.widget.CardView&gt;\r\n\r\n        &lt;androidx.cardview.widget.CardView\r\n            android:id=\"@+id\/cvCompressed\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_height=\"300dp\"\r\n            android:layout_marginStart=\"16dp\"\r\n            app:cardCornerRadius=\"8dp\"\r\n            app:layout_constraintBottom_toTopOf=\"@+id\/compressionSeekBar\"\r\n            app:layout_constraintEnd_toEndOf=\"parent\"\r\n            app:layout_constraintHorizontal_bias=\"0.5\"\r\n            app:layout_constraintStart_toEndOf=\"@+id\/cvOriginal\"\r\n            app:layout_constraintTop_toTopOf=\"parent\"&gt;\r\n\r\n            &lt;ImageView\r\n                android:id=\"@+id\/compressedImageView\"\r\n                android:layout_width=\"match_parent\"\r\n                android:layout_height=\"match_parent\"\r\n                android:scaleType=\"centerCrop\" \/&gt;\r\n\r\n        &lt;\/androidx.cardview.widget.CardView&gt;\r\n\r\n        &lt;ProgressBar\r\n            android:id=\"@+id\/progressBar\"\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:visibility=\"gone\"\r\n            app:layout_constraintBottom_toBottomOf=\"@id\/cvCompressed\"\r\n            app:layout_constraintEnd_toEndOf=\"@id\/cvCompressed\"\r\n            app:layout_constraintStart_toStartOf=\"@id\/cvCompressed\"\r\n            app:layout_constraintTop_toTopOf=\"@id\/cvCompressed\" \/&gt;\r\n\r\n        &lt;SeekBar\r\n            android:id=\"@+id\/compressionSeekBar\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_marginBottom=\"8dp\"\r\n            android:max=\"100\"\r\n            android:progress=\"100\"\r\n            app:layout_constraintBottom_toTopOf=\"@+id\/selectImagesButton\"\r\n            app:layout_constraintEnd_toEndOf=\"parent\"\r\n            app:layout_constraintStart_toStartOf=\"parent\"\r\n            app:layout_constraintTop_toBottomOf=\"@id\/cvCompressed\" \/&gt;\r\n\r\n        &lt;TextView\r\n            android:id=\"@+id\/tvSeekValue\"\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_gravity=\"center_horizontal\"\r\n            android:layout_marginTop=\"8dp\"\r\n            android:layout_marginBottom=\"8dp\"\r\n            android:text=\"100%\"\r\n            app:layout_constraintBottom_toTopOf=\"@id\/selectImagesButton\"\r\n            app:layout_constraintEnd_toEndOf=\"parent\"\r\n            app:layout_constraintStart_toStartOf=\"@id\/compressionSeekBar\"\r\n            app:layout_constraintTop_toBottomOf=\"@id\/compressionSeekBar\" \/&gt;\r\n\r\n        &lt;Button\r\n            android:id=\"@+id\/selectImagesButton\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_marginBottom=\"8dp\"\r\n            android:text=\"Select Image\"\r\n            app:layout_constraintBottom_toTopOf=\"@+id\/compressImages\"\r\n            app:layout_constraintEnd_toEndOf=\"parent\"\r\n            app:layout_constraintStart_toStartOf=\"parent\" \/&gt;\r\n\r\n        &lt;Button\r\n            android:id=\"@+id\/compressImages\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_marginBottom=\"8dp\"\r\n            android:text=\"Compress Image\"\r\n            app:layout_constraintBottom_toTopOf=\"@+id\/saveImagesButton\"\r\n            app:layout_constraintEnd_toEndOf=\"parent\"\r\n            app:layout_constraintStart_toStartOf=\"parent\" \/&gt;\r\n\r\n        &lt;Button\r\n            android:id=\"@+id\/saveImagesButton\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_marginBottom=\"16dp\"\r\n            android:text=\"Save Compressed Image\"\r\n            app:layout_constraintBottom_toBottomOf=\"parent\"\r\n            app:layout_constraintEnd_toEndOf=\"parent\"\r\n            app:layout_constraintStart_toStartOf=\"parent\" \/&gt;\r\n\r\n    &lt;\/androidx.constraintlayout.widget.ConstraintLayout&gt;\r\n\r\n&lt;\/ScrollView&gt;\r\n<\/code><\/pre>\n<hr \/>\n<p><iframe loading=\"lazy\" title=\"Android Image Compressor App with Picker &amp; Storage | Free Source Code | Android Studio #shorts\" width=\"540\" height=\"960\" src=\"https:\/\/www.youtube.com\/embed\/c_VbPUy_tsI?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<hr \/>\n<p><a href=\"https:\/\/github.com\/alsaeeddev\/image-compressor\/archive\/refs\/heads\/main.zip\">Download Source Code<\/a><\/p>\n<h3 style=\"margin-top: 24px;\">\ud83d\udca1 Final Thoughts<\/h3>\n<p>This Android image compressor app provides a practical implementation of media handling, compression, and storage. Whether you&#8217;re building a photo editing app or simply want to reduce image size before upload, this codebase gives you a solid foundation to build on.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re looking to build an Android Image Compressor App with Image Picker that lets users select, compress, and save images directly to their device, you&#8217;re in the right place. In this tutorial, we\u2019ll walk you through how to create an Android image compression tool using Java, ActivityResultLauncher, SeekBar, and a multithreaded approach for performance. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3645,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,117,164],"tags":[59,55,64,52,53,62,61,58,60,54,57,63,65,56],"class_list":["post-3644","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-java","category-source-codes","tag-activityresultlauncher-image-picker","tag-android-bitmap-compress","tag-android-file-outputstream","tag-android-image-compression","tag-android-image-picker","tag-android-image-save-external-storage","tag-android-java-multithreading","tag-android-photo-editor-app","tag-android-seekbar-compression","tag-compress-image-android-java","tag-image-compression-tutorial-android","tag-image-compressor-android-studio","tag-mediastore-android-gallery-update","tag-save-image-to-storage-android"],"_links":{"self":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts\/3644","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/comments?post=3644"}],"version-history":[{"count":20,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts\/3644\/revisions"}],"predecessor-version":[{"id":3989,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts\/3644\/revisions\/3989"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/media\/3645"}],"wp:attachment":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/media?parent=3644"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/categories?post=3644"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/tags?post=3644"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}