Pages

Saturday, September 21, 2013

Text Viewer

TextViewer is a simple app that can be used to view files that store text. For this simple text viewer app, you can view txt, rtf, xml, java, and html files. To start developing this app, you need to create a project called TextViewer in Eclipse.
There are two sub-interfaces in this app. For the first sub-interface, there are one EditText, and one ListView. The EditText allows you to input and displays the part of a directory of file. It is used as a search box in a dictionary. The ListView shows the files and directories of the directory that is shown in the EditText.

TextViewer Files browse interface



While you are making change to the text in the EditText, the contents (files and directories) of the valid directory shown in the EditText are displayed in the ListView. This sub-interface displays immediately when the user clicks the TextViewer app to launch and this is the place that the user can choose a file to open. The class that represents this sub-interface is called BrowseFragment. This class extends the Fragment class. The layout file for this sub-interface is browse_view.xml. Below are the contents of the BrowseFragment.java and browse_view.xml files. If you are new to the uses of Fragment and FragmentAcvitity in Android, please go back to this previous post Login.

BrowseFragent.java file

package com.example.textviewer;

import com.actionbarsherlock.app.SherlockFragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BrowseFragment extends SherlockFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.browse_view, container, false);
}

public static BrowseFragment newInstance(String str){

return(new BrowseFragment());
}
/*
public void onAttach(Activity activity){
super.onAttach(activity);
mAct=activity;
}
*/

public void onStart(){
super.onStart();

}


}


browse_view.xml file

<RelativeLayout 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"
>

    <EditText
        android:id="@+id/txt_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/text_hint" />
     <TextView
        android:id="@+id/txt_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
         android:layout_below="@+id/txt_input"
      />
    <ListView
        android:id="@+id/files_list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/txt_view"
        >
     
    </ListView>

</RelativeLayout>


The strings.xml file that declares strings resources to be used in this app is written as shown below:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">TextViewer</string>
    <string name="action_settings">Settings</string>
    <string name="text_hint">Type a path</string>

</resources>


The second sub-interface that is represented by the ContentFragment class contains a TextViewer to display the content of the selected file. The ContentFragment class also extends the Fragment class. The layout file for this sub-interface is content_view.xml file. Now you take a look at the ContentFragment.java file and then the content of the content_view.xml file.

ContentFragment.java

package com.example.textviewer;

import java.io.FileInputStream;
import java.io.IOException;

import com.actionbarsherlock.app.SherlockFragment;

import android.app.Activity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class ContentFragment extends SherlockFragment{
//@Override
private static String arg;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.content_view, container, false);
}
public static ContentFragment newInstance(String str){
arg=str;
return(new ContentFragment());
}


public void onAttach(Activity activity){
super.onAttach(activity);
}

public void onStart(){
super.onStart();
showContent(arg);
}

public void showContent(String path){

TextView txtview = (TextView)getActivity().findViewById(R.id.cont_view);
txtview.setMovementMethod(new ScrollingMovementMethod());
try{
FileInputStream fs=new FileInputStream(path);
int data=0;
while((data=fs.read())!=-1){
char c=(char)data;
txtview.append(c+"");
}
fs.close();
}catch(IOException ie){}
}



}


In the ContentFragment class, the arg variable is used to store the file path selected by the user. This file path is sent from the main activity that is represented by the MainActivity class discussed later in this post. Once the file path is obtained, the content of the file can be read by invoking the showContent method. FileInputStream class is also available in Android to read content of a binary file.

content_view.xml file

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"  
    android:background="#dfdfdf"    
     >

 <TextView
     android:id="@+id/cont_view"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:maxLines="20"
     android:scrollbars="vertical"
     android:textSize="13sp"
     android:textColor="#000000" />

</RelativeLayout>


Besides launching the TextView app directly from the Android apps launcher, the user can select a text file from Android to view. To make this possible, you have to write some intent filters in AndroidManifest.xml file. The content of the AndroidManifest.xml file is shown below.

AndroidManifest.xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.textviewer"
    android:versionCode="1"
    android:versionName="1.0"
 
     >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application    
        android:allowBackup="true"
        android:icon="@drawable/textview"
        android:label="@string/app_name"
        android:theme="@style/Theme.Sherlock.Light.DarkActionBar"
     
       
         >
        <activity
            android:configChanges="orientation"
            android:name="com.example.textviewer.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.txt"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.rtf"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.java"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.xml"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.html"/>
            <data android:host="*"/>
        </intent-filter>
       
        </activity>
    </application>

</manifest>


Since the TextView app is able to view txt, rtf, xml, java, and html file, you need to have five separate intent filters for these file types. Each intent filter is almost the same, except the path pattern. In each path pattern, different file extension has to be specified for the supported file type. If you are new to the use of intent filter, i recommend you to read the previous post Web Downloader app.

Now we take a look at our main interface. The main interface that is represented by the MainActvity class. This class extends the FragmentAcvity class. On the main interface, the sub-interfaces (BrowseFragment and ContentFragment) are dynamically added and replaced. When the app firstly run (by launching it directly from the Android apps launcher), the first sub-interface is added to the main interface by the code fragment below.

BrowseFragment bf=new BrowseFragment();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,bf).commit();


The first sub-interface is replaced by the second sub-interface, when the user selects a file from the ListView to view  so that the content of the selected file is read in the ContentFragment to display on the TextView, cont_view.

ContentFragment cf=ContentFragment.newInstance(path);
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, cf);
transact.addToBackStack(null);
transact.commit();

As i mentioned about, you can open the TextViewer app by two ways. One way is opening it directly from the Android apps launcher. For the second way, the user can select a file from Android to open it. How do you know which file is selected by the user? Of course, it is simple to understand. When the user select the file, an intent object that stored path of the file is broadcast by Android. Android lists apps that can open the file. Our TextViewer app is capable to open the file because we used intent filters to inform Android already. So Android will show the TextViewer app in its list. To get the intent object sent by Android, you need to use the getIntent method. When you have the intent object, you can get the path of the selected file. And then its content is read and displayed by calling the openFile method. This method subsequently calls the show method. The code fragment below does the job.

Intent intent=getIntent();
String action=intent.getAction();
String type =intent.getType();
if(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_PICK.equals(action) && type!=""){
openFile(intent);

}

MainActivity.java file

package com.example.textviewer;

import java.io.File;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.MenuItem;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends SherlockFragmentActivity {
float nsize=13.0f;
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Get intent object broadcast by other Android
        //when the user selects a file to open from Android
        Intent intent=getIntent();
        String action=intent.getAction();      
        String type =intent.getType();
        if(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_PICK.equals(action) && type!=""){        
        openFile(intent); //open and read the content of the file
       
        }

        //If no intent, display the BrowseFragment to show a list of files and directories.
        //From this interface the user will select a file to view

        else{
        BrowseFragment bf=new BrowseFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,bf).commit();
        }
    }

public void openFile(Intent intent){
String path=intent.getData().getPath();//read file path from the intent
//then show the content of the file
show(path);
}

public void onStart(){
super.onStart();
regControls(); //register event listeners with EditText and ListView
TextView txtview = (TextView) findViewById(R.id.cont_view);
if(txtview==null)
getSherlock().getActionBar().hide(); //hide ActionBar when you are not on the ContentFragment    
else
getSherlock().getActionBar().show(); //show ActionBar when you are on the ContentFragment
//This will show up zoom in and zoom out image icons on the ActionBar
}

//Handle hardware back button press
public void onBackPressed() {
TextView txtview = (TextView) findViewById(R.id.cont_view);
if(txtview!=null){
BrowseFragment df=new  BrowseFragment();
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, df);
transact.addToBackStack(null);
transact.commit();
onStart();
}
else{

System.exit(0);
}
}

public void regControls(){
EditText et=(EditText) findViewById(R.id.txt_input);
if(et!=null){
et.addTextChangedListener(new ChangeListener());//listen to text change event
et.setText("/"); //initialize the path to /
ListView lv=(ListView) findViewById(R.id.files_list);
lv.setOnItemClickListener(new ClickListener());//listen to item click event
}
}

public void zoomin(View view){
TextView tv=(TextView) findViewById(R.id.cont_view);
nsize++;
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP,nsize);

}

public void zoomout(View view){
TextView tv=(TextView) findViewById(R.id.cont_view);
nsize--;
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP,nsize);

}

class ChangeListener implements TextWatcher{
   
    public void beforeTextChanged(CharSequence s, int start, int before, int count){
   
   
    }
    //Making change to text of the EditText, txt_input
    public void onTextChanged(CharSequence s, int start, int before, int count){
    EditText et=(EditText) findViewById(R.id.txt_input);
    String txt=et.getText().toString();
    listDirContents(txt);//display the files and directories in the ListView
    }
   
    public void afterTextChanged(Editable ed){
   
   
    }
    }
 
    class ClickListener implements OnItemClickListener{
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      // selected item
            String selectedItem = ((TextView) view).getText().toString();
            EditText et=(EditText) findViewById(R.id.txt_input);
            et.append("/"+selectedItem);//append selected item to the path
            show(et.getText().toString());//show the content of the input path if it is a file.
         
           
      }
      public void onNothingSelected(AdapterView<?> parent){
     
      }
     
     
      }
 
 
 
    public void listDirContents(String path){
    ListView l=(ListView) findViewById(R.id.files_list);
    if(path!=null){
    try{
    File f=new File(path);    
    if(f!=null){
    String[] contents=f.list();
    if(contents.length>0){
    ArrayAdapter<String> aa=new ArrayAdapter<String>(this,R.layout.listlayout,contents);
    l.setAdapter(aa);
    }
    }
    }catch(Exception e){}
    }
   
 
   
    }

    public void show(String path){
    Toast.makeText(getBaseContext(), "Opening "+path, Toast.LENGTH_SHORT).show();
    try{
    File f=new File(path);
    if(f.isFile()){//display ContentFragment to display the content of the selected file
    ContentFragment cf=ContentFragment.newInstance(path);
    FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
    transact.replace(R.id.fragment_container, cf);
    transact.addToBackStack(null);
    transact.commit();
    getSherlock().getActionBar().show();
    }
    }catch(Exception e){}
    }
 
 
    @Override
    public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
        getSupportMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        TextView txtview = (TextView) findViewById(R.id.cont_view);
     
        if(txtview!=null){
        switch(item.getItemId()){
       
            case R.id.zoomin:
            zoomin(txtview);
                break;

            case R.id.zoomout:
            zoomout(txtview);
                break;

        }
     
        return true;
        }
        else return false;
    }

 
 
}


Set up ActionBar for TextViewer app


ActionBar displays at the top your user interface. Generally, the ActionBar are used to placed some actions that are frequently used. In the TextViewer app, the zoom in and zoom out actions are placed on the ActioBar. The items of the ActionBar are written in the main.xml file (in res/menu directory). This ActionBar is shown only if the second sub-interface (ContentFragment) displays.


Here is the content of the main.xml file.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/Theme.Sherlock" >
    <item
        android:id="@+id/zoomin"
        android:showAsAction="ifRoom|withText"
        android:icon="@drawable/zoomin"
        style="@style/Theme.Sherlock"
    />

    <item
        android:id="@+id/zoomout"
        android:showAsAction="ifRoom|withText"
        android:icon="@drawable/zoomout"
        style="@style/Theme.Sherlock"
    />

</menu>

You will have two small images in res/drawable directory. One represents the zoom in action and another one for zoom out action.
The main activity displays the ActionBar in its onCreateOptionsMenu method. The code fragment below shows this point.

public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
getSupportMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}

You will need to override the onOptionsItemSelected method to determine which item (zoom in or zoom out) of the ActionBar is selected. The switch case statement can be used to check the id of the item that is selected.

The library that is used to set up the ActionBar in the TextViewer is ActionBarSherkLock. The reason to choose this library is that it supports older Android versions (lower than 3.0) and still can be used in newer versions. You can download the ActionBarSherkLock library from the link http://actionbarsherlock.com/download.html. You will get a zip file that contains the ActionBarSherkLock project inside. Extract the zip file and place its contents to a place that you can find it easily.
Now you need to add the ActionBarSherkLock project to the TextViewer app. In the File menu, select New then select Project...When the New Project window displays, Click Android to expand it. Then select Android Project from Existing Code. By clicking the Next button, you will get the Import Projects window. Click the Browse button to find the directory that contains the ActionBarSherkLock project (from the zip file you just extracted).

image actionbarsherklock project


Now the ActionBarSherkLock project is included in the Package Explorer (shown at top left corner).

sherklocklock added to the project


For the next step, you need to add the ActonBarSherkLock project as a library of the TextViewer app. You can accomplish this task by right-click on the TextViewer then selection Android. Click the Add... button to open the Project Selection dialog then select actionbarsherlock. Click Ok to close the dialog. Please the see the picture below to clarify these steps.

Add actionbarsherklock as a library to the TextViewer app


ActionBarSherkLock uses android-support-v4 library jar file. This will conflict with the android-support-v4 libary jar file that you have when you first create the TextView app. To avoid this problem, you need to delete android-support-v4 libary jar file that comes with the TextView app.

Now you are ready to run the TextViewer app. Upload the TextView.apk file and install it on your real device and test it. If your any question regarding this sample app, please leave comments below.

Download apk file of the Text Viewer app

29 comments:

  1. ma đạo sư, tại hoàn mỹ không gian pháp trận cũng vị tất có thể làm được.

    Tử tinh cầu rơi xuống tay Diệp Âm Trúc
    -Đây là đáp án ngươi muốn biết. Pháp lực của ta đương nhiên không đủ, nhưng có vật này thì không gì không thể..

    Thấy tử tinh cầu, Thường Hạo nhất thời sáng ngời con mắt
    -Có thể cho ta xem qua một chút hay không?

    Diệp Âm Trúc gật đầu nói:
    -Đương nhiên.
    Tiện tay đưa tử tinh cầu cho Thường Hạo.
    đồng tâm
    game mu
    cho thuê nhà trọ
    cho thuê phòng trọ
    nhac san cuc manh
    số điện thoại tư vấn pháp luật miễn phí
    văn phòng luật
    tổng đài tư vấn pháp luật
    dịch vụ thành lập công ty
    http://we-cooking.com/
    chém gió
    trung tâm tiếng anh
    Thường Hạo cẩn thận dùng hai tay đỡ lấy tử tinh cầu. Đối với một không gian hệ ma pháp sư như hắn mà nói, còn có cái gì có thể hấp dẫn hắn hơn thượng đẳng không gian hệ ma pháp vật phẩm đây?

    Lúc này mọi người đã trấn tĩnh lại, Hải Dương mặc áo khoác ngoài vào, đưa mắt nhòm quanh. Đây là một huyệt động trống trải chỉ có sáu người bọn họ, mặc dù nơi này rất lạnh, nhưng rất khô ráo.

    ReplyDelete
  2. Astonishing! Much obliged are utilized as a gratefulness, or appreciation which demonstrates the idea of individual. However, I'm extremely respected to have this https://www.capstoneproject.net/capstone-paper-title/ site since I came to think about some more effects of much appreciated. So I value the endeavors you made in composing this article.

    ReplyDelete
  3. شركة تنظيف مكيفات اسبليت بالرياض
    ارقام صيانة مكيفات اسبليت بالرياض
    ادوات تنظيف مكيفات اسبليت
    سعر غسيل مكيف اسبليت بالرياض
    نظافة المكيف من أهم الأشياء التي يجب ان نحرص عليها بشكل كبير فالمكيف من أهم الأشياء التي لا نستطيع الاستغناء عنها خاصة في فصل الصيف وارتفاع درجات الحرارة ولكن يجب الابتعاد عن الهواة في عملية التنظيف واللجوء إلي المتخصصين القادرين علي تنظيف المكيفات بطريقه صحيحة وآمنة تماما وهذا ما تمتاز به شركة غسيل مكيفات بالرياض فهي من أولي الشركات في تنظيف الخزانات فلديها مجموعة من العاملين المميزين في مجال غسيل المكيفات بطريقه محترفة وآمنة

    ReplyDelete
  4. The information you have posted is very useful. Blackmart market helper Freestore

    ReplyDelete
  5. This website is great. I find a lot of useful info.

    ReplyDelete
  6. This is really a great stuff for sharing. Keep it up .Thanks for sharing.
    hotmail sign in

    ReplyDelete
  7. This is really good blog information thanks for sharing .I am really impressed with your writing abilities
    appflix para pc

    ReplyDelete
  8. blackmart apk is app just like app store that gives premium app in free. Blackmart is really amazing apps for gamer.

    ReplyDelete
  9. i found a lot of information here, that i had never seen before. thanks for publish an interesting article. keep it up.

    ReplyDelete
  10. Thanks for the share great info, Very useful done great job posted very useful info. A good blog to read! writing skills are awesome. Thanks and good luck keep posting. If you want to to get paid APKs free you must visit Blackmart

    ReplyDelete
  11. If you want to to get paid APKs free you must visit Blackmart

    ReplyDelete
  12. Wow Amazing site! I’m more than happy to uncover this site. I want to to Thankyou for ones time for this particularly fantastic read!! I definitely liked every bit of it and I also have you book
    marked to see new stuff in your site.
    Blackmart best Apk

    ReplyDelete
  13. wow...it is really amazing thanks for share.

    ReplyDelete
  14. This is really a great stuff for sharing. Keep it up .Thanks for sharing.
    Good web blog

    ReplyDelete
  15. Hey, thank you a lot for sharing this article with us. I can’t say, how grateful we are to read this. Also, I would love to share it with my friends and family, who are interested. I hope you will publish such articles in the future as well. It’s so helpful. Goodbye! Take a look at this wonderful article and download Clash of Clans Mod APK And FilmoraGo Pro APK.

    ReplyDelete
  16. You have given very good information. I was looking for a similar website. As yours I love reading Although nowadays people watch videos more, but I still like to read.


    Shala Darpan

    Shadi Anudan

    Google Mera Naam Kya Hai

    Quarantine Meaning in Hindi

    ReplyDelete
  17. HD Streamz is an android app to enjoy 100+ live channel, radio streamming and movie streamming app for android. Download

    ReplyDelete
  18. ilir. Hangi tamamen ücretsizdir.
    Selçuk Sports Apk Kişisel bilgi

    ReplyDelete
  19. These are truly fantastic ideas regarding blogging. You have touched on some pleasant points here. Any way keep up writing.
    Badabusiness

    ReplyDelete
  20. This is also a very good post which I really enjoyed reading. Thank you for sharing. Users of the Xbox One gaming system can upgrade their controllers. Microsoft frequently changes the gaming accessory's firmware, while they are not necessary. These firmware updates expand the controller in many ways, including capability for headset adapters and many other things. Read more about How to fix Xbox Controller.

    ReplyDelete
  21. I'm in awe of how this article manages to be both concise and comprehensive. It's a testament to the writer's expertise in the subject matter. Another type of colorblindness is blue-yellow color blindness, also known as tritanopia. Individuals with this condition may have difficulty distinguishing between shades of blue and green, as well as between yellow and violet. Go through this article to know more about Types of colorblind.

    ReplyDelete
  22. Kudos to the author for their ability to communicate complex ideas with simplicity. This article is a testament to their skillful writing. If your phone's vibration is not working, make sure to check the settings and ensure that the vibration option is enabled. Sometimes it can accidentally get turned off. Visit this article to know more about Fix phone vibration issue.

    ReplyDelete
  23. If you want to bypass FRP you can Download Apks and tools from maracutenga infocell so recebe to unlock Android devices without using a Google account.

    ReplyDelete