Austin Huang
4 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 617 additions and 196 deletions
-
42app/src/main/java/awais/instagrabber/adapters/KeywordsFilterAdapter.java
-
51app/src/main/java/awais/instagrabber/adapters/viewholder/dialogs/KeywordsFilterDialogViewHolder.java
-
5app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
-
2app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
-
101app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
-
2app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
-
12app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
-
10app/src/main/java/awais/instagrabber/customviews/emoji/Emoji.java
-
16app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java
-
78app/src/main/java/awais/instagrabber/dialogs/KeywordsFilterDialog.java
-
1app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
-
91app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
-
25app/src/main/java/awais/instagrabber/fragments/settings/PostPreferencesFragment.java
-
3app/src/main/java/awais/instagrabber/utils/Constants.java
-
49app/src/main/java/awais/instagrabber/utils/KeywordsFilterUtils.java
-
55app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
-
20app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
52app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.java
-
44app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.java
-
12app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java
-
19app/src/main/java/awais/instagrabber/viewmodels/AppStateViewModel.java
-
22app/src/main/java/awais/instagrabber/webservices/GraphQLService.java
-
12app/src/main/java/awais/instagrabber/webservices/NewsService.java
-
57app/src/main/res/layout/dialog_keywords_filter.xml
-
23app/src/main/res/layout/item_keyword.xml
-
5app/src/main/res/values/strings.xml
@ -0,0 +1,42 @@ |
|||||
|
package awais.instagrabber.adapters; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.adapters.viewholder.dialogs.KeywordsFilterDialogViewHolder; |
||||
|
|
||||
|
public class KeywordsFilterAdapter extends RecyclerView.Adapter<KeywordsFilterDialogViewHolder> { |
||||
|
|
||||
|
private final Context context; |
||||
|
private final ArrayList<String> items; |
||||
|
|
||||
|
public KeywordsFilterAdapter(Context context, ArrayList<String> items){ |
||||
|
this.context = context; |
||||
|
this.items = items; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public KeywordsFilterDialogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |
||||
|
final View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_keyword, parent, false); |
||||
|
return new KeywordsFilterDialogViewHolder(v); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBindViewHolder(@NonNull KeywordsFilterDialogViewHolder holder, int position) { |
||||
|
holder.bind(items, position, context, this); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int getItemCount() { |
||||
|
return items.size(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
package awais.instagrabber.adapters.viewholder.dialogs; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.view.View; |
||||
|
import android.widget.Button; |
||||
|
import android.widget.TextView; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashSet; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.adapters.KeywordsFilterAdapter; |
||||
|
import awais.instagrabber.utils.Constants; |
||||
|
import awais.instagrabber.utils.SettingsHelper; |
||||
|
|
||||
|
public class KeywordsFilterDialogViewHolder extends RecyclerView.ViewHolder { |
||||
|
|
||||
|
private final Button deleteButton; |
||||
|
private final TextView item; |
||||
|
|
||||
|
public KeywordsFilterDialogViewHolder(@NonNull View itemView) { |
||||
|
super(itemView); |
||||
|
deleteButton = itemView.findViewById(R.id.keyword_delete); |
||||
|
item = itemView.findViewById(R.id.keyword_text); |
||||
|
} |
||||
|
|
||||
|
public void bind(ArrayList<String> items, int position, Context context, KeywordsFilterAdapter adapter){ |
||||
|
item.setText(items.get(position)); |
||||
|
deleteButton.setOnClickListener(view -> { |
||||
|
final String s = items.get(position); |
||||
|
SettingsHelper settingsHelper = new SettingsHelper(context); |
||||
|
items.remove(position); |
||||
|
settingsHelper.putStringSet(Constants.KEYWORD_FILTERS, new HashSet<>(items)); |
||||
|
adapter.notifyDataSetChanged(); |
||||
|
final String message = context.getString(R.string.removed_keywords, s); |
||||
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public Button getDeleteButton(){ |
||||
|
return deleteButton; |
||||
|
} |
||||
|
|
||||
|
public TextView getTextView(){ |
||||
|
return item; |
||||
|
} |
||||
|
} |
@ -1,101 +0,0 @@ |
|||||
package awais.instagrabber.asyncs; |
|
||||
|
|
||||
import android.os.AsyncTask; |
|
||||
import android.util.Log; |
|
||||
|
|
||||
import androidx.annotation.Nullable; |
|
||||
|
|
||||
import awais.instagrabber.interfaces.FetchListener; |
|
||||
import awais.instagrabber.repositories.responses.FriendshipStatus; |
|
||||
import awais.instagrabber.repositories.responses.User; |
|
||||
import awais.instagrabber.webservices.GraphQLService; |
|
||||
import awais.instagrabber.webservices.ServiceCallback; |
|
||||
import awais.instagrabber.webservices.UserService; |
|
||||
|
|
||||
public final class ProfileFetcher extends AsyncTask<Void, Void, Void> { |
|
||||
private static final String TAG = ProfileFetcher.class.getSimpleName(); |
|
||||
private final UserService userService; |
|
||||
private final GraphQLService graphQLService; |
|
||||
|
|
||||
private final FetchListener<User> fetchListener; |
|
||||
private final long myId; |
|
||||
private final boolean isLoggedIn; |
|
||||
private final String userName; |
|
||||
|
|
||||
public ProfileFetcher(final String userName, |
|
||||
final long myId, |
|
||||
final boolean isLoggedIn, |
|
||||
final FetchListener<User> fetchListener) { |
|
||||
this.userName = userName; |
|
||||
this.myId = myId; |
|
||||
this.isLoggedIn = isLoggedIn; |
|
||||
this.fetchListener = fetchListener; |
|
||||
userService = isLoggedIn ? UserService.getInstance() : null; |
|
||||
graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); |
|
||||
} |
|
||||
|
|
||||
@Nullable |
|
||||
@Override |
|
||||
protected Void doInBackground(final Void... voids) { |
|
||||
if (isLoggedIn && userName != null) { |
|
||||
userService.getUsernameInfo(userName, new ServiceCallback<User>() { |
|
||||
@Override |
|
||||
public void onSuccess(final User user) { |
|
||||
userService.getUserFriendship(user.getPk(), new ServiceCallback<FriendshipStatus>() { |
|
||||
@Override |
|
||||
public void onSuccess(final FriendshipStatus status) { |
|
||||
user.setFriendshipStatus(status); |
|
||||
fetchListener.onResult(user); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(final Throwable t) { |
|
||||
Log.e(TAG, "Error", t); |
|
||||
fetchListener.onFailure(t); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(final Throwable t) { |
|
||||
Log.e(TAG, "Error", t); |
|
||||
fetchListener.onFailure(t); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
else if (isLoggedIn) { |
|
||||
userService.getUserInfo(myId, new ServiceCallback<User>() { |
|
||||
@Override |
|
||||
public void onSuccess(final User user) { |
|
||||
fetchListener.onResult(user); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(final Throwable t) { |
|
||||
Log.e(TAG, "Error", t); |
|
||||
fetchListener.onFailure(t); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
else { |
|
||||
graphQLService.fetchUser(userName, new ServiceCallback<User>() { |
|
||||
@Override |
|
||||
public void onSuccess(final User user) { |
|
||||
fetchListener.onResult(user); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(final Throwable t) { |
|
||||
Log.e(TAG, "Error", t); |
|
||||
fetchListener.onFailure(t); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected void onPreExecute() { |
|
||||
if (fetchListener != null) fetchListener.doBefore(); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,78 @@ |
|||||
|
package awais.instagrabber.dialogs; |
||||
|
|
||||
|
import android.app.Dialog; |
||||
|
import android.content.Context; |
||||
|
import android.os.Bundle; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.view.Window; |
||||
|
import android.widget.EditText; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.fragment.app.DialogFragment; |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashSet; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.adapters.KeywordsFilterAdapter; |
||||
|
import awais.instagrabber.databinding.DialogKeywordsFilterBinding; |
||||
|
import awais.instagrabber.utils.Constants; |
||||
|
import awais.instagrabber.utils.SettingsHelper; |
||||
|
import awais.instagrabber.utils.Utils; |
||||
|
|
||||
|
public final class KeywordsFilterDialog extends DialogFragment { |
||||
|
|
||||
|
@Override |
||||
|
public void onStart() { |
||||
|
super.onStart(); |
||||
|
final Dialog dialog = getDialog(); |
||||
|
if (dialog == null) return; |
||||
|
final Window window = dialog.getWindow(); |
||||
|
if (window == null) return; |
||||
|
final int height = ViewGroup.LayoutParams.WRAP_CONTENT; |
||||
|
final int width = (int) (Utils.displayMetrics.widthPixels * 0.8); |
||||
|
window.setLayout(width, height); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |
||||
|
final DialogKeywordsFilterBinding dialogKeywordsFilterBinding = DialogKeywordsFilterBinding.inflate(inflater, container, false); |
||||
|
init(dialogKeywordsFilterBinding, getContext()); |
||||
|
dialogKeywordsFilterBinding.btnOK.setOnClickListener(view -> this.dismiss()); |
||||
|
return dialogKeywordsFilterBinding.getRoot(); |
||||
|
} |
||||
|
|
||||
|
private void init(DialogKeywordsFilterBinding dialogKeywordsFilterBinding, Context context){ |
||||
|
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context); |
||||
|
final RecyclerView recyclerView = dialogKeywordsFilterBinding.recyclerKeyword; |
||||
|
recyclerView.setLayoutManager(linearLayoutManager); |
||||
|
|
||||
|
final SettingsHelper settingsHelper = new SettingsHelper(context); |
||||
|
final ArrayList<String> items = new ArrayList<>(settingsHelper.getStringSet(Constants.KEYWORD_FILTERS)); |
||||
|
final KeywordsFilterAdapter adapter = new KeywordsFilterAdapter(context, items); |
||||
|
recyclerView.setAdapter(adapter); |
||||
|
|
||||
|
final EditText editText = dialogKeywordsFilterBinding.editText; |
||||
|
|
||||
|
dialogKeywordsFilterBinding.btnAdd.setOnClickListener(view ->{ |
||||
|
final String s = editText.getText().toString(); |
||||
|
if(s.isEmpty()) return; |
||||
|
if(items.contains(s)) { |
||||
|
editText.setText(""); |
||||
|
return; |
||||
|
} |
||||
|
items.add(s.toLowerCase()); |
||||
|
settingsHelper.putStringSet(Constants.KEYWORD_FILTERS, new HashSet<>(items)); |
||||
|
adapter.notifyItemInserted(items.size()); |
||||
|
final String message = context.getString(R.string.added_keywords, s); |
||||
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); |
||||
|
editText.setText(""); |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
package awais.instagrabber.utils; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.repositories.responses.Caption; |
||||
|
import awais.instagrabber.repositories.responses.Media; |
||||
|
|
||||
|
public final class KeywordsFilterUtils { |
||||
|
|
||||
|
private final ArrayList<String> keywords; |
||||
|
|
||||
|
public KeywordsFilterUtils(final ArrayList<String> keywords){ |
||||
|
this.keywords = keywords; |
||||
|
} |
||||
|
|
||||
|
public boolean filter(final String caption){ |
||||
|
if(caption == null) return false; |
||||
|
if(keywords.isEmpty()) return false; |
||||
|
final String temp = caption.toLowerCase(); |
||||
|
for(final String s:keywords){ |
||||
|
if(temp.contains(s)) return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public boolean filter(final Media media){ |
||||
|
if(media == null) return false; |
||||
|
final Caption c = media.getCaption(); |
||||
|
if(c == null) return false; |
||||
|
if(keywords.isEmpty()) return false; |
||||
|
final String temp = c.getText().toLowerCase(); |
||||
|
for(final String s:keywords){ |
||||
|
if(temp.contains(s)) return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public List<Media> filter(final List<Media> media){ |
||||
|
if(keywords.isEmpty()) return media; |
||||
|
if(media == null) return new ArrayList<>(); |
||||
|
|
||||
|
final List<Media> result= new ArrayList<>(); |
||||
|
for(final Media m:media){ |
||||
|
if(!filter(m)) result.add(m); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
package awais.instagrabber.utils.emoji; |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.google.gson.JsonDeserializationContext; |
||||
|
import com.google.gson.JsonDeserializer; |
||||
|
import com.google.gson.JsonElement; |
||||
|
import com.google.gson.JsonObject; |
||||
|
import com.google.gson.JsonParseException; |
||||
|
|
||||
|
import java.lang.reflect.Type; |
||||
|
import java.util.LinkedHashMap; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import awais.instagrabber.customviews.emoji.Emoji; |
||||
|
import awais.instagrabber.customviews.emoji.EmojiCategory; |
||||
|
import awais.instagrabber.customviews.emoji.EmojiCategoryType; |
||||
|
|
||||
|
public class EmojiCategoryDeserializer implements JsonDeserializer<EmojiCategory> { |
||||
|
private static final String TAG = EmojiCategoryDeserializer.class.getSimpleName(); |
||||
|
|
||||
|
@Override |
||||
|
public EmojiCategory deserialize(final JsonElement json, |
||||
|
final Type typeOfT, |
||||
|
final JsonDeserializationContext context) throws JsonParseException { |
||||
|
final JsonObject jsonObject = json.getAsJsonObject(); |
||||
|
final JsonElement typeElement = jsonObject.get("type"); |
||||
|
final JsonObject emojisObject = jsonObject.getAsJsonObject("emojis"); |
||||
|
if (typeElement == null || emojisObject == null) { |
||||
|
throw new JsonParseException("Invalid json for EmojiCategory"); |
||||
|
} |
||||
|
final String typeString = typeElement.getAsString(); |
||||
|
EmojiCategoryType type; |
||||
|
try { |
||||
|
type = EmojiCategoryType.valueOf(typeString); |
||||
|
} catch (IllegalArgumentException e) { |
||||
|
Log.e(TAG, "deserialize: ", e); |
||||
|
type = EmojiCategoryType.OTHERS; |
||||
|
} |
||||
|
final Map<String, Emoji> emojis = new LinkedHashMap<>(); |
||||
|
for (final Map.Entry<String, JsonElement> emojiObjectEntry : emojisObject.entrySet()) { |
||||
|
final String unicode = emojiObjectEntry.getKey(); |
||||
|
final JsonElement value = emojiObjectEntry.getValue(); |
||||
|
if (unicode == null || value == null) { |
||||
|
throw new JsonParseException("Invalid json for EmojiCategory"); |
||||
|
} |
||||
|
final Emoji emoji = context.deserialize(value, Emoji.class); |
||||
|
emojis.put(unicode, emoji); |
||||
|
} |
||||
|
return new EmojiCategory(type, emojis); |
||||
|
} |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
package awais.instagrabber.utils.emoji; |
||||
|
|
||||
|
import com.google.gson.JsonArray; |
||||
|
import com.google.gson.JsonDeserializationContext; |
||||
|
import com.google.gson.JsonDeserializer; |
||||
|
import com.google.gson.JsonElement; |
||||
|
import com.google.gson.JsonObject; |
||||
|
import com.google.gson.JsonParseException; |
||||
|
|
||||
|
import java.lang.reflect.Type; |
||||
|
import java.util.LinkedList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.customviews.emoji.Emoji; |
||||
|
|
||||
|
public class EmojiDeserializer implements JsonDeserializer<Emoji> { |
||||
|
@Override |
||||
|
public Emoji deserialize(final JsonElement json, |
||||
|
final Type typeOfT, |
||||
|
final JsonDeserializationContext context) throws JsonParseException { |
||||
|
final JsonObject jsonObject = json.getAsJsonObject(); |
||||
|
final JsonElement unicodeElement = jsonObject.get("unicode"); |
||||
|
final JsonElement nameElement = jsonObject.get("name"); |
||||
|
if (unicodeElement == null || nameElement == null) { |
||||
|
throw new JsonParseException("Invalid json for Emoji class"); |
||||
|
} |
||||
|
final JsonElement variantsElement = jsonObject.get("variants"); |
||||
|
final List<Emoji> variants = new LinkedList<>(); |
||||
|
if (variantsElement != null) { |
||||
|
final JsonArray variantsArray = variantsElement.getAsJsonArray(); |
||||
|
for (final JsonElement variantElement : variantsArray) { |
||||
|
final Emoji variant = context.deserialize(variantElement, Emoji.class); |
||||
|
if (variant != null) { |
||||
|
variants.add(variant); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return new Emoji( |
||||
|
unicodeElement.getAsString(), |
||||
|
nameElement.getAsString(), |
||||
|
variants |
||||
|
); |
||||
|
} |
||||
|
} |
@ -0,0 +1,57 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<ScrollView 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" |
||||
|
android:padding="16dp"> |
||||
|
|
||||
|
<androidx.constraintlayout.widget.ConstraintLayout |
||||
|
android:id="@+id/root" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:animateLayoutChanges="true" |
||||
|
android:paddingTop="16dp"> |
||||
|
|
||||
|
<androidx.appcompat.widget.AppCompatEditText |
||||
|
android:id="@+id/edit_text" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:hint="@string/hint_keyword" |
||||
|
android:singleLine="true" |
||||
|
app:layout_constraintEnd_toStartOf="@id/btnAdd" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" /> |
||||
|
|
||||
|
<Button |
||||
|
android:id="@+id/btnAdd" |
||||
|
android:layout_width="30dp" |
||||
|
android:layout_height="30dp" |
||||
|
android:background="@drawable/ic_add" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintStart_toEndOf="@id/edit_text" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
app:layout_constraintBottom_toBottomOf="@id/edit_text" /> |
||||
|
|
||||
|
<androidx.recyclerview.widget.RecyclerView |
||||
|
android:id="@+id/recyclerKeyword" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
app:layout_constraintTop_toBottomOf="@id/btnAdd" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
tools:layout_editor_absoluteX="16dp" |
||||
|
tools:listitem="@layout/item_keyword" /> |
||||
|
|
||||
|
<androidx.appcompat.widget.AppCompatButton |
||||
|
android:id="@+id/btnOK" |
||||
|
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_gravity="end" |
||||
|
android:text="@string/ok" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintTop_toBottomOf="@id/recyclerKeyword" /> |
||||
|
|
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
||||
|
</ScrollView> |
@ -0,0 +1,23 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:gravity="center_vertical" |
||||
|
android:orientation="horizontal" |
||||
|
android:paddingBottom="8dp" |
||||
|
android:paddingTop="8dp"> |
||||
|
|
||||
|
<androidx.appcompat.widget.AppCompatTextView |
||||
|
android:id="@+id/keyword_text" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_weight="1" /> |
||||
|
|
||||
|
<Button |
||||
|
android:id="@+id/keyword_delete" |
||||
|
android:layout_width="30dp" |
||||
|
android:layout_height="30dp" |
||||
|
android:background="@drawable/ic_delete" |
||||
|
android:scaleType="center" /> |
||||
|
|
||||
|
</LinearLayout> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue