diff --git a/app/MyHyvesBookPlusStagram/app/build.gradle b/app/MyHyvesBookPlusStagram/app/build.gradle index 907707c..85f6d09 100644 --- a/app/MyHyvesBookPlusStagram/app/build.gradle +++ b/app/MyHyvesBookPlusStagram/app/build.gradle @@ -26,10 +26,11 @@ dependencies { }) compile 'com.android.support:appcompat-v7:25.3.1' compile 'com.android.support:design:25.3.1' - compile 'com.google.firebase:firebase-database:11.0.1' - compile 'com.google.firebase:firebase-auth:11.0.1' compile 'com.android.support.constraint:constraint-layout:1.0.2' compile 'com.android.support:support-v4:25.3.1' + + compile 'com.google.firebase:firebase-database:11.0.1' + compile 'com.google.firebase:firebase-auth:11.0.1' compile 'com.google.firebase:firebase-storage:11.0.1' // FirebaseUI Storage only diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/CameraFragment.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/CameraFragment.java index 8ff1e89..14c8297 100644 --- a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/CameraFragment.java +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/CameraFragment.java @@ -16,6 +16,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.RelativeLayout; +import nl.myhyvesbookplus.tagram.controller.UploadClass; + /** * A simple {@link Fragment} subclass. * Activities that contain this fragment must implement the diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/MainActivity.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/MainActivity.java index 053408f..51b3a3b 100644 --- a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/MainActivity.java +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/MainActivity.java @@ -3,12 +3,11 @@ package nl.myhyvesbookplus.tagram; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Intent; -import android.hardware.Camera; +import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.BottomNavigationView; -import android.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.MenuItem; @@ -16,7 +15,10 @@ import android.view.View; import com.google.firebase.auth.FirebaseAuth; -public class MainActivity extends AppCompatActivity implements CameraFragment.OnFragmentInteractionListener, ProfileFragment.OnFragmentInteractionListener, TimelineFragment.OnFragmentInteractionListener { +import nl.myhyvesbookplus.tagram.controller.UploadClass; +import nl.myhyvesbookplus.tagram.model.BitmapPost; + +public class MainActivity extends AppCompatActivity implements CameraFragment.OnFragmentInteractionListener, ProfileFragment.OnFragmentInteractionListener, TimelineFragment.OnFragmentInteractionListener, UploadClass.ProfilePictureUpdatedListener { final static private String TAG = "MainScreen"; FirebaseAuth mAuth; @@ -32,25 +34,25 @@ public class MainActivity extends AppCompatActivity implements CameraFragment.On case nl.myhyvesbookplus.tagram.R.id.navigation_timeline: Log.d(TAG, "onNavigationItemSelected: Timeline"); TimelineFragment timeline = new TimelineFragment(); - transaction.replace(R.id.content, timeline); - transaction.addToBackStack(null); - transaction.commit(); + transaction.replace(R.id.content, timeline) + .addToBackStack(null) + .commit(); return true; case nl.myhyvesbookplus.tagram.R.id.navigation_camera: Log.d(TAG, "onNavigationItemSelected: Camera"); CameraFragment camera = new CameraFragment(); - transaction.replace(R.id.content, camera); - transaction.addToBackStack(null); - transaction.commit(); + transaction.replace(R.id.content, camera) + .addToBackStack(null) + .commit(); return true; case nl.myhyvesbookplus.tagram.R.id.navigation_profile: Log.d(TAG, "onNavigationItemSelected: Profile"); ProfileFragment profile = new ProfileFragment(); - transaction.replace(R.id.content, profile); - transaction.addToBackStack(null); - transaction.commit(); + transaction.replace(R.id.content, profile) + .addToBackStack(null) + .commit(); return true; } return false; @@ -76,7 +78,6 @@ public class MainActivity extends AppCompatActivity implements CameraFragment.On FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.replace(R.id.content, fragment); transaction.commit(); - } @Override @@ -92,11 +93,31 @@ public class MainActivity extends AppCompatActivity implements CameraFragment.On public void logOutOnClick(View view) { FirebaseAuth.getInstance().signOut(); goToLogin(); - this.finish(); } protected void goToLogin() { Intent goToLogIn = new Intent(this, LoginActivity.class); startActivity(goToLogIn); + this.finish(); } + + public void testCreatePost(View view) { + UploadClass uploadClass = new UploadClass(this); + Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8); + BitmapPost bitmapPost = new BitmapPost(bitmap, "Dit is een Test!"); + uploadClass.uploadPicture(bitmapPost); + } + + @Override + public void ProfilePictureUpdated(Boolean success) { + Log.d(TAG, "ProfilePictureUpdated: Ja ik luister naar je!"); + FragmentManager man = getFragmentManager(); + ProfileFragment frag = (ProfileFragment) man.findFragmentById(R.id.content); + FragmentTransaction transaction = man.beginTransaction(); + transaction.detach(frag) + .attach(frag) + .commit(); + Log.d(TAG, "ProfilePictureUpdated: Done reloading fragment"); + } + } diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/ProfileFragment.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/ProfileFragment.java index ff753af..5f87eb2 100644 --- a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/ProfileFragment.java +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/ProfileFragment.java @@ -1,19 +1,20 @@ package nl.myhyvesbookplus.tagram; +import android.app.Fragment; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; -import android.app.Fragment; +import android.provider.MediaStore; import android.support.annotation.NonNull; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageButton; -import android.widget.TextView; import android.widget.ImageView; +import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; @@ -25,9 +26,9 @@ import com.google.firebase.auth.FirebaseUser; import com.google.firebase.storage.FirebaseStorage; import com.google.firebase.storage.StorageReference; -import org.w3c.dom.Text; +import nl.myhyvesbookplus.tagram.controller.UploadClass; -import static android.content.ContentValues.TAG; +import static android.app.Activity.RESULT_OK; /** * A simple {@link Fragment} subclass. @@ -38,25 +39,25 @@ import static android.content.ContentValues.TAG; * create an instance of this fragment. */ public class ProfileFragment extends Fragment implements View.OnClickListener { + static final int REQUEST_IMAGE_CAPTURE = 1; + final static private String TAG = "ProfileFragment"; // 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; - - /// Views /// - + private static Uri downloadUrl; protected Button changePwdButton; protected ImageButton profilePicButton; + + /// Views /// protected StorageReference httpsReference; protected TextView profileName; protected ImageView profilePicture; - - private OnFragmentInteractionListener mListener; protected FirebaseUser user; + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + private OnFragmentInteractionListener mListener; /// Required empty public constructor /// @@ -90,9 +91,6 @@ public class ProfileFragment extends Fragment implements View.OnClickListener { } user = FirebaseAuth.getInstance().getCurrentUser(); - if (user != null && user.getPhotoUrl() != null) { - httpsReference = FirebaseStorage.getInstance().getReferenceFromUrl(user.getPhotoUrl().toString()); - } } /** @@ -120,11 +118,16 @@ public class ProfileFragment extends Fragment implements View.OnClickListener { View view = inflater.inflate(R.layout.fragment_profile, container, false); findViews(view); + if (user != null && user.getPhotoUrl() != null) { + httpsReference = FirebaseStorage.getInstance().getReferenceFromUrl(user.getPhotoUrl().toString()); + } + if (httpsReference != null) { Glide.with(this).using(new FirebaseImageLoader()).load(httpsReference).into(profilePicture); } - // TODO Remove check for getDisplayName if all users are required to enter a displayName anyways. + profilePicture.invalidate(); + if (user != null && user.getDisplayName() != null) { profileName.setText(user.getDisplayName()); } @@ -161,9 +164,38 @@ public class ProfileFragment extends Fragment implements View.OnClickListener { * Performs profile picture change action. */ public void profilePicOnClick() { - Log.d(TAG, "profilePicOnClick: JE KAN NOG GEEN FOTO UPLOADEN"); +// Log.d(TAG, "profilePicOnClick: JE KAN NOG GEEN FOTO UPLOADEN"); + dispatchTakePictureIntent(); } + /** + * Starts new intent for access to the built-in camera of device. + */ + private void dispatchTakePictureIntent() { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) { + startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); + } + } + + /** + * Grabs the image just taken by the built-in camera and pushes this image to the user account. + * @param requestCode + * @param resultCode + * @param data + */ + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { + Bundle extras = data.getExtras(); + Bitmap imageBitmap = (Bitmap) extras.get("data"); + UploadClass uploadClass = new UploadClass(getActivity()); + uploadClass.uploadProfilePicture(imageBitmap); + } + } + + + // TODO Make this function into its own class for modularity. /** * Performs password reset action. @@ -221,4 +253,6 @@ public class ProfileFragment extends Fragment implements View.OnClickListener { // TODO: Update argument type and name void onFragmentInteraction(Uri uri); } + + } diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/UploadClass.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/UploadClass.java deleted file mode 100644 index daf20bb..0000000 --- a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/UploadClass.java +++ /dev/null @@ -1,62 +0,0 @@ -package nl.myhyvesbookplus.tagram; - -import android.graphics.Bitmap; -import android.net.Uri; -import android.support.annotation.NonNull; - -import com.google.android.gms.tasks.OnFailureListener; -import com.google.android.gms.tasks.OnSuccessListener; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.auth.FirebaseUser; -import com.google.firebase.storage.FirebaseStorage; -import com.google.firebase.storage.StorageReference; -import com.google.firebase.storage.UploadTask; - -import java.io.ByteArrayOutputStream; -import java.util.Random; - -/** - * Created by marijnjansen on 20/06/2017. - */ - -public class UploadClass { - - private StorageReference mStorageRef; - - - public UploadClass() { - String name = Integer.toString(new Random().nextInt(10000)); - mStorageRef = FirebaseStorage.getInstance().getReference().child("images/" + name); - } - - private byte[] bitmapToBytes(Bitmap bitmap) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); - return baos.toByteArray(); - } - - public void uploadPicture(Bitmap bitmap) { - UploadTask uploadTask = mStorageRef.putBytes(bitmapToBytes(bitmap)); - uploadTask.addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - - } - }) - .addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - // Handle successful uploads on complete - Uri downloadUrl = taskSnapshot.getMetadata().getDownloadUrl(); - } - }); - } - - private String getUserUid() { - FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); - if (user != null) { - return user.getUid(); - } - return ""; - } -} diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/controller/DownloadClass.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/controller/DownloadClass.java new file mode 100644 index 0000000..1ddaf17 --- /dev/null +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/controller/DownloadClass.java @@ -0,0 +1,28 @@ +package nl.myhyvesbookplus.tagram.controller; + +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.storage.FirebaseStorage; +import com.google.firebase.storage.StorageReference; + +import nl.myhyvesbookplus.tagram.model.UriPost; + +/** + * Created by marijnjansen on 23/06/2017. + */ + +public class DownloadClass { + private static final String TAG = "DownloadClass"; + private StorageReference mStorageRef; + private DatabaseReference mDataRef; + + public DownloadClass() { + mStorageRef = FirebaseStorage.getInstance().getReference(); + mDataRef = FirebaseDatabase.getInstance().getReference(); + } + + public UriPost[] getPosts() { + UriPost[] posts = new UriPost[10]; + return posts; + } +} diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/controller/UploadClass.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/controller/UploadClass.java new file mode 100644 index 0000000..3f104a2 --- /dev/null +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/controller/UploadClass.java @@ -0,0 +1,132 @@ +package nl.myhyvesbookplus.tagram.controller; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.auth.UserProfileChangeRequest; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.storage.FirebaseStorage; +import com.google.firebase.storage.StorageReference; +import com.google.firebase.storage.UploadTask; + +import java.io.ByteArrayOutputStream; + +import nl.myhyvesbookplus.tagram.model.BitmapPost; +import nl.myhyvesbookplus.tagram.model.UriPost; + +import static java.lang.System.currentTimeMillis; + +/** + * Class that does all the photo uploading things. + */ +public class UploadClass { + + private static final String TAG = "UploadClass"; + private StorageReference mStorageRef; + private DatabaseReference mDataRef; + + private ProfilePictureUpdatedListener mListener; + + public UploadClass(Context context) { + mStorageRef = FirebaseStorage.getInstance().getReference(); + mDataRef = FirebaseDatabase.getInstance().getReference(); + mListener = (ProfilePictureUpdatedListener) context; + } + + /// Helpers /// + + private byte[] bitmapToBytes(Bitmap bitmap) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); + return baos.toByteArray(); + } + + private String getUserUid() { + FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); + if (user != null) { + return user.getUid(); + } + return ""; + } + + /// Post Uploads /// + + public void uploadPicture(final BitmapPost post) { + final String name = getUserUid() + currentTimeMillis(); + + UploadTask uploadTask = mStorageRef.child("posts").child(name + ".jpg").putBytes(bitmapToBytes(post.getBitmap())); + uploadTask.addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + Log.d(TAG, "onFailure: Upload Failed"); + } + }) + .addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { + // Handle successful uploads on complete + Log.d(TAG, "onSuccess: Upload Success!"); + Uri downloadUrl = taskSnapshot.getMetadata().getDownloadUrl(); + putPostInDatabase(post.getUriPost(downloadUrl), name); + } + }); + } + + private void putPostInDatabase(UriPost post, String name) { + DatabaseReference ref = mDataRef.child("posts").child(name); + ref.setValue(post) + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (task.isSuccessful()) { + Log.d(TAG, "onComplete: Added post to database"); + } else { + Log.d(TAG, "onComplete: " + task.getException().getLocalizedMessage()); + } + } + }); + } + + /// Profile picture /// + + public void uploadProfilePicture(Bitmap picture) { + byte[] uploadPhoto = bitmapToBytes(picture); + UploadTask photoUpload = mStorageRef.child("profile").child(getUserUid() + "_" + currentTimeMillis()).putBytes(uploadPhoto); + photoUpload.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { + Uri downloadUrl = taskSnapshot.getDownloadUrl(); + updateProfilePictureInUser(downloadUrl); + } + }); + } + + private void updateProfilePictureInUser(Uri url) { + FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); + UserProfileChangeRequest request = new UserProfileChangeRequest.Builder() + .setPhotoUri(url) + .build(); + user.updateProfile(request) + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + Log.d(TAG, "onComplete: Updated profile picture"); + mListener.ProfilePictureUpdated(true); + } + }); + } + + public interface ProfilePictureUpdatedListener { + void ProfilePictureUpdated(Boolean success); + } +} diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/BitmapPost.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/BitmapPost.java new file mode 100644 index 0000000..83d8399 --- /dev/null +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/BitmapPost.java @@ -0,0 +1,35 @@ +package nl.myhyvesbookplus.tagram.model; + +import android.graphics.Bitmap; +import android.net.Uri; + +import java.util.Date; + +/** + * BitmapPost is a Class for a Post with a Bitmap as an image. + */ +public class BitmapPost extends Post { + private Bitmap photo; + + public BitmapPost(Bitmap photo, String comment, Date date, int nietSlechts, String poster) { + super(comment, date, nietSlechts, poster); + this.photo = photo; + } + + public BitmapPost(Bitmap photo, String comment) { + super(comment); + this.photo = photo; + } + + public Bitmap getBitmap() { + return photo; + } + + public UriPost getUriPost(Uri url) { + return new UriPost(url, getComment(), getDate(), getNietSlechts(), getPoster()); + } + + public void setPhoto(Bitmap photo) { + this.photo = photo; + } +} \ No newline at end of file diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/Post.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/Post.java new file mode 100644 index 0000000..87edbdd --- /dev/null +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/Post.java @@ -0,0 +1,63 @@ +package nl.myhyvesbookplus.tagram.model; + +import com.google.firebase.auth.FirebaseAuth; + +import java.util.Date; + +/** + * Post is a Class for a Post with a Bitmap as an image. + */ +abstract class Post { + + private Date date; + private String comment; + private int nietSlechts; + private String poster; + + Post() { + // Default constructor required for calls to Post.getValue(User.class) + } + + Post(String comment, Date date, int nietSlechts, String poster) { + this.date = date; + this.comment = comment; + this.nietSlechts = nietSlechts; + this.poster = poster; + } + + Post(String comment) { + this(comment, new Date(), 0, FirebaseAuth.getInstance().getCurrentUser().getUid()); + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public int getNietSlechts() { + return nietSlechts; + } + + public void setNietSlechts(int nietSlechts) { + this.nietSlechts = nietSlechts; + } + + public String getPoster() { + return poster; + } + + public void setPoster(String poster) { + this.poster = poster; + } +} \ No newline at end of file diff --git a/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/UriPost.java b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/UriPost.java new file mode 100644 index 0000000..2b80ad6 --- /dev/null +++ b/app/MyHyvesBookPlusStagram/app/src/main/java/nl/myhyvesbookplus/tagram/model/UriPost.java @@ -0,0 +1,35 @@ +package nl.myhyvesbookplus.tagram.model; + +import android.net.Uri; + +import java.util.Date; + +/** + * UriPost is a Class for a Post with a Uri as an image. + */ +public class UriPost extends Post { + private String photo; + + public UriPost() { + // Default constructor required for calls to DataSnapshot.getValue(UriPost.class) + + } + + public UriPost(Uri photo, String comment, Date date, int nietSlechts, String poster) { + super(comment, date, nietSlechts, poster); + this.photo = photo.toString(); + } + + public UriPost(String photo, String comment) { + super(comment); + this.photo = photo; + } + + public String getUri() { + return photo; + } + + public void setPhoto(String photo) { + this.photo = photo; + } +} \ No newline at end of file diff --git a/app/MyHyvesBookPlusStagram/app/src/main/res/layout/fragment_timeline.xml b/app/MyHyvesBookPlusStagram/app/src/main/res/layout/fragment_timeline.xml index 629607b..09f32ca 100644 --- a/app/MyHyvesBookPlusStagram/app/src/main/res/layout/fragment_timeline.xml +++ b/app/MyHyvesBookPlusStagram/app/src/main/res/layout/fragment_timeline.xml @@ -9,5 +9,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/hello_blank_fragment" /> +