浏览代码

添加修改背景功能UI

dengyujia 1 年之前
父节点
当前提交
8b13659980

+ 9 - 3
app/src/main/AndroidManifest.xml

@@ -19,7 +19,7 @@
         android:theme="@style/AppTheme"
         tools:targetApi="31">
         <activity
-            android:name=".activities.HeaderFooterActivity"
+            android:name=".activities.BackgroundActivity"
             android:exported="true">
 
             <intent-filter>
@@ -33,13 +33,20 @@
                 android:value="" />
         </activity>
         <activity
-            android:name=".activities.WatermarkActivity"
+            android:name=".activities.HeaderFooterActivity"
             android:exported="true">
 
             <meta-data
                 android:name="android.app.lib_name"
                 android:value="" />
         </activity>
+        <activity
+            android:name=".activities.WatermarkActivity"
+            android:exported="true">
+            <meta-data
+                android:name="android.app.lib_name"
+                android:value="" />
+        </activity>
 
         <meta-data
             android:name="compdfkit_license"
@@ -48,5 +55,4 @@
             android:name="compdfkit_message"
             android:value="mG0c3O3Mzeu5dkZJW3gpqotjgd+APU/4fMqIHQR4gdwQurxTxGuBlGAhs0P1mD3XKqvsLa4npVLfI/mo45dkJ5QFcNndGDskT70Jc2iwVo5hrp9sGpqfp0B228KI+IMTu4aGVjtYuk+Uxs/kosIBw1367/WkJ00tM7U7tttD6ccsvUiEnSpVC16x66CLYBR9nL4G5ayY1Yw9IbVniEuzaCCVg1bqAdLSXAjS4mxI/QshmpPEP0qS8yPf877NfXnm6gW/UFOMJ5eWQQ5IO+aCVQ==" />
     </application>
-
 </manifest>

+ 181 - 0
app/src/main/java/com/compdfkit/pdfviewer/activities/BackgroundActivity.java

@@ -0,0 +1,181 @@
+package com.compdfkit.pdfviewer.activities;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.view.MenuProvider;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.compdfkit.core.common.CPDFDocumentException;
+import com.compdfkit.core.document.CPDFBackground;
+import com.compdfkit.core.document.CPDFDocument;
+import com.compdfkit.core.page.CPDFPage;
+import com.compdfkit.pdfviewer.R;
+import com.compdfkit.pdfviewer.entity.BackgroundSetting;
+import com.compdfkit.pdfviewer.tools.DialogManager;
+
+import java.io.File;
+import java.util.Random;
+
+public class BackgroundActivity extends AppCompatActivity implements MenuProvider {
+    private int pageCount = 0;
+
+    private View pageContainer;
+    private ImageView pageImage;
+
+    private int currentPageHeight = 0;
+    private int currentPageWidth = 0;
+
+    private int imageHeight;
+    private int imageWidth;
+
+    private CPDFDocument document = new CPDFDocument(this);
+
+    private float sizeConvertRate = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        int[] themes = new int[] {R.style.AppTheme, R.style.AppRedTheme, R.style.AppOrangeTheme, R.style.AppGreenTheme, R.style.AppCyanBlueTheme};
+        int randomTheme = new Random().nextInt(5);
+        setTheme(themes[randomTheme]);
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_background);
+
+        Toolbar toolbar = findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+        addMenuProvider(this);
+
+        File PDFFile = new File("/storage/emulated/0/TestPDFs/test.pdf");
+        Uri PDFUri = Uri.fromFile(PDFFile);
+
+        reloadDocument(PDFFile, PDFUri);
+
+        pageContainer = findViewById(R.id.page_container);
+
+        pageImage = findViewById(R.id.page_image);
+        pageImage.post(this::initPageImage);
+
+        if (this.getResources().getConfiguration().uiMode == 0x11) {
+            WindowInsetsControllerCompat wic = ViewCompat.getWindowInsetsController(getWindow().getDecorView());
+            if (wic != null)
+                wic.setAppearanceLightStatusBars(true);
+        }
+    }
+
+    @Override
+    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
+        menuInflater.inflate(R.menu.menu_background, menu);
+    }
+
+    @Override
+    public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
+        return false;
+    }
+
+    private void initPageImage() {
+        CPDFPage currentPage = document.pageAtIndex(0);
+
+        RectF pageSize = document.getPageSize(0);
+        float pageW = pageSize.right - pageSize.left;
+        float pageH = pageSize.bottom - pageSize.top;
+
+        if (pageW / pageH >  ((float) pageContainer.getWidth() / (float) pageContainer.getHeight())) {
+            sizeConvertRate = pageContainer.getWidth() / pageW;
+
+            imageHeight = (int) (sizeConvertRate * pageH);
+            imageWidth = pageContainer.getWidth();
+
+            pageImage.setMaxHeight(imageHeight);
+            pageImage.setMaxWidth(imageWidth);
+        } else {
+            sizeConvertRate = pageContainer.getHeight() / pageH;
+
+            imageHeight = pageContainer.getHeight();
+            imageWidth = (int) (sizeConvertRate * pageW);
+
+            pageImage.setMaxWidth(imageWidth);
+        }
+
+        currentPageHeight = imageHeight;
+        currentPageWidth = imageWidth;
+        Bitmap pageBitmap = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_4444);
+
+        currentPage.renderPage(pageBitmap, 0, 0, imageWidth, imageHeight, 0xFFFFFFFF, 255, 0, true, true);
+        refreshPageImage(pageBitmap);
+    }
+
+    public void reloadDocument(@NonNull File PDFFile, Uri PDFUri) {
+        CPDFDocument.PDFDocumentError error = document.open(PDFFile.getAbsolutePath());
+
+        if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorPassword) {
+            error = document.open(PDFUri, "password");
+        } else if (error != CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess) {
+            Toast.makeText(this, "File failed.", Toast.LENGTH_SHORT).show();
+        } else {
+            pageCount = document.getPageCount();
+        }
+    }
+
+    //TODO: Replace the same code.
+    public void reloadDocument() throws CPDFDocumentException {
+        if (document.hasChanges()) {
+            boolean res;
+            res = document.save();
+        }
+        document.close();
+
+        File PDFFile = new File("/storage/emulated/0/TestPDFs/test.pdf");
+        Uri PDFUri = Uri.fromFile(PDFFile);
+
+        reloadDocument(PDFFile, PDFUri);
+        CPDFPage currentPage = document.pageAtIndex(0);
+
+        Bitmap pageBitmap = Bitmap.createBitmap(currentPageWidth, currentPageHeight, Bitmap.Config.ARGB_4444);
+        currentPage.renderPage(pageBitmap, 0, 0, currentPageWidth, currentPageHeight, 0xFFFFFFFF, 255, 0, true, true);
+        refreshPageImage(pageBitmap);
+    }
+
+    public void refreshPageImage(Bitmap updatePageBitmap) {
+        pageImage.setImageBitmap(updatePageBitmap);
+    }
+
+    public void showSetting(MenuItem menuItem) {
+        DialogManager.showBackgroundSettingDialog(this);
+    }
+
+    public void finishEdit(MenuItem menuItem) throws CPDFDocumentException {
+        CPDFBackground background = document.getBackground();
+
+        BackgroundSetting setting = new BackgroundSetting();
+        setting.setColor(background.getColor());
+        setting.setOpacity(background.getOpacity());
+        setting.setRotation(background.getRotation());
+        setting.setxOffset(background.getXOffset());
+        setting.setyOffset(background.getYOffset());
+        setting.setPages(background.getPages());
+        setting.setScale(background.getScale());
+
+        background.setColor(0xFF66CCFF);
+        background.setOpacity(0.f);
+        background.setRotation(0.f);
+        background.setXOffset(0.f);
+        background.setPages("0-4");
+        background.setScale(1.f);
+        background.update();
+
+        reloadDocument();
+    }
+}

+ 0 - 2
app/src/main/java/com/compdfkit/pdfviewer/activities/HeaderFooterActivity.java

@@ -102,7 +102,6 @@ public class HeaderFooterActivity extends AppCompatActivity implements MenuProvi
         initHeaderFooterViews();
 
         Log.d("TestBatesString", TextGenerator.generateBatesText("NB", "BN", 3, 20));
-//        Log.d("TestDateString", TextGenerator.generateDateText(10));
     }
 
     @Override
@@ -261,7 +260,6 @@ public class HeaderFooterActivity extends AppCompatActivity implements MenuProvi
             CPDFBates bates = document.getBates();
             HeaderFooterHandle.updateBatesType(bates);
         }
-
         reloadDocument();
     }
 

+ 89 - 0
app/src/main/java/com/compdfkit/pdfviewer/entity/BackgroundSetting.java

@@ -0,0 +1,89 @@
+package com.compdfkit.pdfviewer.entity;
+
+import android.graphics.Bitmap;
+
+public class BackgroundSetting {
+    private int color;
+    private float opacity;
+    private String pages;
+    private float rotation;
+    private float scale;
+    private float xOffset;
+    private float yOffset;
+    private Bitmap image;
+
+    public BackgroundSetting() {
+        color = 0xFFFFFFFF;
+        opacity = 1.0f;
+        pages = "0";
+        rotation = 0.f;
+        scale = 1.0f;
+        xOffset = 0.f;
+        yOffset = 0.f;
+        image = null;
+    }
+
+    public int getColor() {
+        return color;
+    }
+
+    public void setColor(int color) {
+        this.color = color;
+    }
+
+    public float getOpacity() {
+        return opacity;
+    }
+
+    public void setOpacity(float opacity) {
+        this.opacity = opacity;
+    }
+
+    public String getPages() {
+        return pages;
+    }
+
+    public void setPages(String pages) {
+        this.pages = pages;
+    }
+
+    public float getRotation() {
+        return rotation;
+    }
+
+    public void setRotation(float rotation) {
+        this.rotation = rotation;
+    }
+
+    public float getScale() {
+        return scale;
+    }
+
+    public void setScale(float scale) {
+        this.scale = scale;
+    }
+
+    public float getxOffset() {
+        return xOffset;
+    }
+
+    public void setxOffset(float xOffset) {
+        this.xOffset = xOffset;
+    }
+
+    public float getyOffset() {
+        return yOffset;
+    }
+
+    public void setyOffset(float yOffset) {
+        this.yOffset = yOffset;
+    }
+
+    public Bitmap getImage() {
+        return image;
+    }
+
+    public void setImage(Bitmap image) {
+        this.image = image;
+    }
+}

+ 66 - 0
app/src/main/java/com/compdfkit/pdfviewer/fragments/BackgroundConsoleFragment.java

@@ -0,0 +1,66 @@
+package com.compdfkit.pdfviewer.fragments;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.compdfkit.pdfviewer.R;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link BackgroundConsoleFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class BackgroundConsoleFragment extends Fragment {
+
+    // TODO: Rename parameter arguments, choose names that match
+    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
+    private static final String ARG_PARAM1 = "param1";
+    private static final String ARG_PARAM2 = "param2";
+
+    // TODO: Rename and change types of parameters
+    private String mParam1;
+    private String mParam2;
+
+    public BackgroundConsoleFragment() {
+        // Required empty public constructor
+    }
+
+    /**
+     * Use this factory method to create a new instance of
+     * this fragment using the provided parameters.
+     *
+     * @param param1 Parameter 1.
+     * @param param2 Parameter 2.
+     * @return A new instance of fragment BackgroundConsoleFragment.
+     */
+    // TODO: Rename and change types and number of parameters
+    public static BackgroundConsoleFragment newInstance(String param1, String param2) {
+        BackgroundConsoleFragment fragment = new BackgroundConsoleFragment();
+        Bundle args = new Bundle();
+        args.putString(ARG_PARAM1, param1);
+        args.putString(ARG_PARAM2, param2);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getArguments() != null) {
+            mParam1 = getArguments().getString(ARG_PARAM1);
+            mParam2 = getArguments().getString(ARG_PARAM2);
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        return inflater.inflate(R.layout.fragment_background_console, container, false);
+    }
+}

+ 43 - 4
app/src/main/java/com/compdfkit/pdfviewer/tools/DialogManager.java

@@ -10,6 +10,7 @@ import android.widget.AdapterView;
 import android.widget.AutoCompleteTextView;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.ImageButton;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AlertDialog;
@@ -25,12 +26,30 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.textfield.TextInputEditText;
 
 public class DialogManager {
+    public static void showBackgroundSettingDialog(Context context) {
+        LayoutInflater inflater = LayoutInflater.from(context);
+        final ConstraintLayout globalSettingLayout = (ConstraintLayout) inflater.inflate(R.layout.background_setting_panel, null);
+        final Button[] positiveButton = new Button[1];
+
+        MaterialAlertDialogBuilder inputDialog = new MaterialAlertDialogBuilder(context);
+        inputDialog.setTitle("Background setting").setView(globalSettingLayout);
+        inputDialog.setPositiveButton(R.string.done, (dialogInterface, i) -> {});
+
+        inputDialog.setNegativeButton(R.string.cancel, (dialogInterface, i) -> {});
+
+        AlertDialog dialog = inputDialog.create();
+        dialog.show();
+        positiveButton[0] = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+    }
 
     public static void showGlobalSettingDialog(Context context, int type) {
         LayoutInflater inflater = LayoutInflater.from(context);
         final ConstraintLayout globalSettingLayout = (ConstraintLayout) inflater.inflate(R.layout.global_setting_panel, null);
         final Button[] positiveButton = new Button[1];
 
+        HeaderFooterActivity activity = (HeaderFooterActivity) context;
+        boolean isCustomRange;
+
         TextInputEditText pageOffsetEdit = globalSettingLayout.findViewById(R.id.start_page);
         pageOffsetEdit.setText(String.valueOf(HeaderFooterHandle.getSettingPageOffset(type)));
 
@@ -45,6 +64,16 @@ public class DialogManager {
         TextInputEditText rightMargin = globalSettingLayout.findViewById(R.id.right_margin);
         rightMargin.setText(String.valueOf(currentMargin.right));
 
+        ImageButton pageRangeBtn = globalSettingLayout.findViewById(R.id.page_range_button);
+        TextView pageRangeMsg = globalSettingLayout.findViewById(R.id.page_range_msg);
+        isCustomRange = !HeaderFooterHandle.getSettingPages(type).equals("0-" + (activity.getPageCount() - 1));
+
+        if (isCustomRange)
+            pageRangeMsg.setText(HeaderFooterHandle.getSettingPages(type));
+
+        boolean finalIsCustomRange = isCustomRange;
+        pageRangeBtn.setOnClickListener((view) -> showPageRangeDialog(context, type, finalIsCustomRange, HeaderFooterHandle.getSettingPages(type), pageRangeMsg));
+
         MaterialAlertDialogBuilder inputDialog = new MaterialAlertDialogBuilder(context);
         inputDialog.setTitle("Global header-footer setting").setView(globalSettingLayout);
         inputDialog.setPositiveButton(R.string.done, (dialogInterface, i) -> {
@@ -56,7 +85,6 @@ public class DialogManager {
             RectF margin = new RectF(left, top, right, bottom);
             HeaderFooterHandle.setSettingMargin(type, margin);
 
-            HeaderFooterActivity activity = (HeaderFooterActivity) context;
             activity.setHeaderFooterViewsMargin(margin);
 
             int pageOffset = Integer.parseInt(pageOffsetEdit.getText().toString());
@@ -74,7 +102,7 @@ public class DialogManager {
         String[] dateFormats = context.getResources().getStringArray(R.array.date_format);
         String[] pageFormats = context.getResources().getStringArray(R.array.page_format);
 
-        final int[] tempFormatIndex = {HeaderFooterHandle.getIndexDateFormat(index), HeaderFooterHandle.getIndexPageFormat(index) };
+        final int[] tempFormatIndex = { HeaderFooterHandle.getIndexDateFormat(index), HeaderFooterHandle.getIndexPageFormat(index) };
 
         LayoutInflater inflater = LayoutInflater.from(context);
         final ConstraintLayout commonSettingLayout = (ConstraintLayout) inflater.inflate(R.layout.common_setting_panel, null);
@@ -172,11 +200,13 @@ public class DialogManager {
         positiveButton[0] = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
     }
 
-    public static void showPageRangeDialog(Context context, boolean isCustomRange, String pageRange) {
+    public static void showPageRangeDialog(Context context, int type, boolean isCustomRange, String pageRange, TextView textView) {
         LayoutInflater inflater = LayoutInflater.from(context);
         final ConstraintLayout rangeSelectLayout = (ConstraintLayout) inflater.inflate(R.layout.range_selector, null);
         final Button[] positiveButton = new Button[1];
 
+        HeaderFooterActivity activity = (HeaderFooterActivity) context;
+
         AppCompatRadioButton customRadioBtn = rangeSelectLayout.findViewById(R.id.custom_range);
         customRadioBtn.setChecked(isCustomRange);
 
@@ -187,7 +217,16 @@ public class DialogManager {
 
         MaterialAlertDialogBuilder inputDialog = new MaterialAlertDialogBuilder(context);
         inputDialog.setTitle(R.string.setting_range).setView(rangeSelectLayout);
-        inputDialog.setPositiveButton(R.string.done, (dialogInterface, i) -> {});
+
+        inputDialog.setPositiveButton(R.string.done, (dialogInterface, i) -> {
+            if (customRadioBtn.isChecked()) {
+                HeaderFooterHandle.setSettingPages(type, rangeEditText.getText().toString());
+                textView.setText(rangeEditText.getText().toString());
+            } else {
+                HeaderFooterHandle.setSettingPages(type, "0-" + (activity.getPageCount() - 1));
+                textView.setText(R.string.all_pages);
+            }
+        });
 
         AlertDialog dialog = inputDialog.create();
         dialog.show();

+ 5 - 0
app/src/main/res/drawable-v31/ic_baseline_rotate_right_20.xml

@@ -0,0 +1,5 @@
+<vector android:autoMirrored="true" android:height="20dp"
+    android:tint="?android:attr/textColorPrimary" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="20dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M15.55,5.55L11,1v3.07C7.06,4.56 4,7.92 4,12s3.05,7.44 7,7.93v-2.02c-2.84,-0.48 -5,-2.94 -5,-5.91s2.16,-5.43 5,-5.91L11,10l4.55,-4.45zM19.93,11c-0.17,-1.39 -0.72,-2.73 -1.62,-3.89l-1.42,1.42c0.54,0.75 0.88,1.6 1.02,2.47h2.02zM13,17.9v2.02c1.39,-0.17 2.74,-0.71 3.9,-1.61l-1.44,-1.44c-0.75,0.54 -1.59,0.89 -2.46,1.03zM16.89,15.48l1.42,1.41c0.9,-1.16 1.45,-2.5 1.62,-3.89h-2.02c-0.14,0.87 -0.48,1.72 -1.02,2.48z"/>
+</vector>

+ 5 - 0
app/src/main/res/drawable/ic_baseline_rotate_right_20.xml

@@ -0,0 +1,5 @@
+<vector android:autoMirrored="true" android:height="20dp"
+    android:tint="#3C4045" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="20dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M15.55,5.55L11,1v3.07C7.06,4.56 4,7.92 4,12s3.05,7.44 7,7.93v-2.02c-2.84,-0.48 -5,-2.94 -5,-5.91s2.16,-5.43 5,-5.91L11,10l4.55,-4.45zM19.93,11c-0.17,-1.39 -0.72,-2.73 -1.62,-3.89l-1.42,1.42c0.54,0.75 0.88,1.6 1.02,2.47h2.02zM13,17.9v2.02c1.39,-0.17 2.74,-0.71 3.9,-1.61l-1.44,-1.44c-0.75,0.54 -1.59,0.89 -2.46,1.03zM16.89,15.48l1.42,1.41c0.9,-1.16 1.45,-2.5 1.62,-3.89h-2.02c-0.14,0.87 -0.48,1.72 -1.02,2.48z"/>
+</vector>

+ 61 - 0
app/src/main/res/layout-v31/activity_background.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <View
+        android:id="@+id/page_container"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:background="?attr/colorSecondaryContainer"
+        app:layout_constraintTop_toBottomOf="@id/toolbar"
+        app:layout_constraintBottom_toTopOf="@id/console_view"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <androidx.appcompat.widget.AppCompatImageView
+        android:id="@+id/page_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:srcCompat="@drawable/pdf"
+        app:layout_constraintTop_toBottomOf="@id/toolbar"
+        app:layout_constraintBottom_toTopOf="@id/console_view"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:contentDescription="@string/page_image" />
+
+    <com.google.android.material.appbar.MaterialToolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?actionBarSize"
+        android:background="?attr/colorSurface"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:title="Edit Background" />
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="409dp"
+        android:layout_height="1dp"
+        android:background="?attr/colorSecondaryContainer"
+        app:layout_constraintBottom_toTopOf="@id/console_view"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <androidx.fragment.app.FragmentContainerView
+        android:name="com.compdfkit.pdfviewer.fragments.BackgroundConsoleFragment"
+        android:id="@+id/console_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:tag="console_fragment"
+        android:elevation="4dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        tools:layout="@layout/fragment_background_console" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 26 - 0
app/src/main/res/layout-v31/fragment_background_console.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="4dp"
+    android:background="?attr/colorSurface"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <com.compdfkit.pdfviewer.customview.picker.ColorPicker
+        android:id="@+id/color_picker"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <com.compdfkit.pdfviewer.customview.picker.ImagePicker
+        android:id="@+id/image_picker"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@id/color_picker"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 9 - 0
app/src/main/res/layout/activity_background.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".activities.BackgroundActivity">
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 211 - 0
app/src/main/res/layout/background_setting_panel.xml

@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/font_div"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:paddingRight="4dp"
+        app:layout_constraintBottom_toTopOf="@id/opacity_slider"
+        app:layout_constraintEnd_toStartOf="@id/font_size_div"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/font_menu"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="XOffset"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/font_picker"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:inputType="none"
+                android:maxLines="1"
+                app:simpleItems="@array/font_family_items" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/font_size_div"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:paddingLeft="4dp"
+        app:layout_constraintBottom_toBottomOf="@+id/font_div"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@id/font_div"
+        app:layout_constraintTop_toTopOf="@+id/font_div">
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/font_size_menu"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="YOffset"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/font_size_picker"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:inputType="none"
+                app:simpleItems="@array/font_size_items" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <ImageView
+        android:id="@+id/opacity_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/opacity_icon"
+        android:src="@drawable/ic_baseline_opacity_20"
+        android:padding="8dp"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
+        app:layout_constraintEnd_toStartOf="@+id/opacity_slider"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@id/opacity_slider"
+        app:layout_constraintBottom_toBottomOf="@id/opacity_slider"/>
+
+    <com.google.android.material.slider.Slider
+        android:id="@+id/opacity_slider"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/opacity"
+        android:value="50.0"
+        android:valueFrom="0.0"
+        android:valueTo="100.0"
+        app:layout_constraintBottom_toTopOf="@id/size_slider"
+        app:layout_constraintEnd_toStartOf="@id/opacity_text"
+        app:layout_constraintStart_toEndOf="@+id/opacity_icon"
+        app:layout_constraintTop_toBottomOf="@+id/font_div" />
+
+    <TextView
+        android:id="@+id/opacity_text"
+        android:layout_width="40dp"
+        android:layout_height="wrap_content"
+        android:text="@string/default_opacity"
+        app:layout_constraintTop_toTopOf="@id/opacity_icon"
+        app:layout_constraintBottom_toBottomOf="@id/opacity_icon"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/opacity_slider" />
+
+
+    <ImageView
+        android:id="@+id/size_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/opacity_icon"
+        android:src="@drawable/ic_baseline_format_size_20"
+        android:padding="8dp"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
+        app:layout_constraintEnd_toStartOf="@+id/size_slider"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@id/size_slider"
+        app:layout_constraintBottom_toBottomOf="@id/size_slider"/>
+
+    <com.google.android.material.slider.Slider
+        android:id="@+id/size_slider"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/size"
+        android:value="100.0"
+        android:valueFrom="50.0"
+        android:valueTo="150.0"
+        app:layout_constraintBottom_toTopOf="@id/rotation_slider"
+        app:layout_constraintEnd_toStartOf="@id/size_text"
+        app:layout_constraintStart_toEndOf="@+id/size_icon"
+        app:layout_constraintTop_toBottomOf="@+id/opacity_slider" />
+
+    <TextView
+        android:id="@+id/size_text"
+        android:layout_width="40dp"
+        android:layout_height="wrap_content"
+        android:text="@string/default_scale"
+        app:layout_constraintTop_toTopOf="@id/size_icon"
+        app:layout_constraintBottom_toBottomOf="@id/size_icon"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/size_slider" />
+
+    <ImageView
+        android:id="@+id/rotation_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/opacity_icon"
+        android:src="@drawable/ic_baseline_rotate_right_20"
+        android:padding="8dp"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
+        app:layout_constraintEnd_toStartOf="@+id/rotation_slider"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@id/rotation_slider"
+        app:layout_constraintBottom_toBottomOf="@id/rotation_slider"/>
+
+    <com.google.android.material.slider.Slider
+        android:id="@+id/rotation_slider"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/size"
+        android:value="0.0"
+        android:valueFrom="-180.0"
+        android:valueTo="180.0"
+        app:layout_constraintBottom_toTopOf="@id/page_range_text"
+        app:layout_constraintEnd_toStartOf="@id/rotation_text"
+        app:layout_constraintStart_toEndOf="@+id/rotation_icon"
+        app:layout_constraintTop_toBottomOf="@+id/size_slider" />
+
+    <TextView
+        android:id="@+id/rotation_text"
+        android:layout_width="40dp"
+        android:layout_height="wrap_content"
+        android:text="0°"
+        app:layout_constraintTop_toTopOf="@id/rotation_icon"
+        app:layout_constraintBottom_toBottomOf="@id/rotation_icon"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/rotation_slider" />
+
+    <TextView
+        android:id="@+id/page_range_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/page_range"
+        android:textSize="20sp"
+        android:textColor="#3C4045"
+        android:padding="12dp"
+        app:layout_constraintTop_toBottomOf="@id/rotation_slider"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/page_range_msg"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/all_pages"
+        android:textColor="@color/black"
+        app:layout_constraintTop_toTopOf="@id/page_range_text"
+        app:layout_constraintBottom_toBottomOf="@id/page_range_text"
+        app:layout_constraintEnd_toStartOf="@id/page_range_button" />
+
+    <ImageButton
+        android:padding="8dp"
+        android:id="@+id/page_range_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="#00000000"
+        android:src="@drawable/ic_baseline_expand_more_24"
+        app:layout_constraintBottom_toBottomOf="@id/page_range_text"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="@id/page_range_text"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 14 - 0
app/src/main/res/layout/fragment_background_console.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".fragments.BackgroundConsoleFragment">
+
+    <!-- TODO: Update blank fragment layout -->
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/hello_blank_fragment" />
+
+</FrameLayout>

+ 17 - 0
app/src/main/res/menu/menu_background.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item
+        android:id="@+id/edit"
+        android:icon="@drawable/ic_baseline_settings_24"
+        android:title="Global Header-Footer Setting"
+        android:onClick="showSetting"
+        app:showAsAction="always"/>
+
+    <item
+        android:id="@+id/done"
+        android:icon="@drawable/ic_baseline_done_24"
+        android:title="@string/done"
+        android:onClick="finishEdit"
+        app:showAsAction="always"/>
+</menu>