added video playback and upload

removed hardwired Bundle arguments
This commit is contained in:
NudeDude 2019-06-01 14:17:58 +02:00
parent 552edfb3a7
commit 8bd32b8806
47 changed files with 966 additions and 493 deletions

View File

@ -5,7 +5,7 @@ android {
compileSdkVersion 28
defaultConfig {
applicationId "org.nuclearfog.twidda"
applicationId 'org.nuclearfog.twidda'
minSdkVersion 16
targetSdkVersion 28
versionCode 1
@ -15,8 +15,8 @@ android {
buildTypes {
all {
buildConfigField "String", "API_KEY_1", TWITTER_CONSUMER_KEY
buildConfigField "String", "API_KEY_2", TWITTER_CONSUMER_SECRET
buildConfigField 'String', 'API_KEY_1', TWITTER_CONSUMER_KEY
buildConfigField 'String', 'API_KEY_2', TWITTER_CONSUMER_SECRET
}
release {
debuggable false
@ -25,7 +25,7 @@ android {
}
debug {
minifyEnabled false
applicationIdSuffix ".debug"
applicationIdSuffix '.debug'
versionNameSuffix '.DEBUG'
}
}
@ -45,6 +45,6 @@ dependencies {
implementation 'org.twitter4j:twitter4j-core:4.0.7'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.github.QuadFlask:colorpicker:0.0.13'
implementation 'com.github.nuclearfog:ZoomView:1.0.1'
implementation 'com.github.nuclearfog:ZoomView:1.0.2'
implementation 'com.github.nuclearfog:Tagger:1'
}

View File

@ -84,7 +84,7 @@
android:theme="@style/AppTheme" />
<activity
android:name=".window.ImageDetail"
android:name=".window.MediaViewer"
android:screenOrientation="portrait"
android:theme="@style/HalfTransparency" />

View File

@ -23,6 +23,10 @@ import org.nuclearfog.twidda.window.SearchPage;
import org.nuclearfog.twidda.window.TweetPopup;
import org.nuclearfog.twidda.window.UserProfile;
import static org.nuclearfog.twidda.window.SearchPage.KEY_SEARCH;
import static org.nuclearfog.twidda.window.UserProfile.KEY_PROFILE_ID;
import static org.nuclearfog.twidda.window.UserProfile.KEY_PROFILE_NAME;
/**
* Main Activity
*/
@ -30,6 +34,7 @@ public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSe
private static final int LOGIN = 1;
private static final int SETTING = 2;
private static final int[] ICONS = {R.drawable.home, R.drawable.hash, R.drawable.mention};
private GlobalSettings settings;
private FragmentAdapter adapter;
@ -66,8 +71,8 @@ public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSe
protected void onStart() {
super.onStart();
if (!settings.getLogin()) {
Intent i = new Intent(this, LoginPage.class);
startActivityForResult(i, LOGIN);
Intent loginIntent = new Intent(this, LoginPage.class);
startActivityForResult(loginIntent, LOGIN);
}
}
@ -97,7 +102,7 @@ public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSe
@Override
public boolean onQueryTextSubmit(String s) {
Intent search = new Intent(MainActivity.this, SearchPage.class);
search.putExtra("search", s);
search.putExtra(KEY_SEARCH, s);
startActivity(search);
return false;
}
@ -126,12 +131,14 @@ public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSe
setting.setVisible(false);
search.collapseActionView();
break;
case 1:
profile.setVisible(false);
search.setVisible(true);
tweet.setVisible(false);
setting.setVisible(true);
break;
case 2:
profile.setVisible(false);
search.setVisible(false);
@ -150,14 +157,16 @@ public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSe
case R.id.action_profile:
long homeId = settings.getUserId();
Intent user = new Intent(this, UserProfile.class);
user.putExtra("userID", homeId);
user.putExtra("username", "");
user.putExtra(KEY_PROFILE_ID, homeId);
user.putExtra(KEY_PROFILE_NAME, "");
startActivity(user);
break;
case R.id.action_tweet:
Intent tweet = new Intent(this, TweetPopup.class);
startActivity(tweet);
break;
case R.id.action_settings:
Intent settings = new Intent(this, AppSettings.class);
startActivityForResult(settings, SETTING);
@ -196,15 +205,14 @@ public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSe
private void initView() {
final int[] icons = {R.drawable.home, R.drawable.hash, R.drawable.mention};
View root = findViewById(R.id.main_layout);
root.setBackgroundColor(settings.getBackgroundColor());
tab.setSelectedTabIndicatorColor(settings.getHighlightColor());
for (int i = 0; i < icons.length; i++) {
for (int i = 0; i < ICONS.length; i++) {
Tab t = tab.getTabAt(i);
if (t != null)
t.setIcon(icons[i]);
t.setIcon(ICONS[i]);
}
}
}

View File

@ -14,6 +14,11 @@ import org.nuclearfog.twidda.fragment.TweetListFragment.TweetType;
import org.nuclearfog.twidda.fragment.UserListFragment;
import org.nuclearfog.twidda.fragment.UserListFragment.UserType;
import static org.nuclearfog.twidda.fragment.TweetListFragment.KEY_FRAG_TWEET_FIX;
import static org.nuclearfog.twidda.fragment.TweetListFragment.KEY_FRAG_TWEET_ID;
import static org.nuclearfog.twidda.fragment.TweetListFragment.KEY_FRAG_TWEET_MODE;
import static org.nuclearfog.twidda.fragment.TweetListFragment.KEY_FRAG_TWEET_SEARCH;
public class FragmentAdapter extends FragmentPagerAdapter {
public enum AdapterType {
@ -27,7 +32,6 @@ public class FragmentAdapter extends FragmentPagerAdapter {
RETWEETER_PAGE,
FAVOR_PAGE
}
private final Fragment[] fragments;
public FragmentAdapter(FragmentManager fManager, AdapterType mode, long id, String search) {
@ -36,10 +40,10 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case HOME_TAB:
Bundle home_tl = new Bundle();
Bundle ment_tl = new Bundle();
home_tl.putSerializable("mode", TweetType.HOME);
ment_tl.putSerializable("mode", TweetType.MENT);
home_tl.putBoolean("fix", true);
ment_tl.putBoolean("fix", true);
home_tl.putSerializable(KEY_FRAG_TWEET_MODE, TweetType.HOME);
ment_tl.putSerializable(KEY_FRAG_TWEET_MODE, TweetType.MENT);
home_tl.putBoolean(KEY_FRAG_TWEET_FIX, true);
ment_tl.putBoolean(KEY_FRAG_TWEET_FIX, true);
fragments = new Fragment[3];
fragments[0] = new TweetListFragment();
fragments[1] = new TrendListFragment();
@ -51,12 +55,12 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case PROFILE_TAB:
Bundle usr_tweet = new Bundle();
Bundle usr_favor = new Bundle();
usr_tweet.putLong("id", id);
usr_favor.putLong("id", id);
usr_tweet.putBoolean("fix", false);
usr_tweet.putBoolean("fix", false);
usr_tweet.putSerializable("mode", TweetType.USER_TWEET);
usr_favor.putSerializable("mode", TweetType.USER_FAVOR);
usr_tweet.putLong(KEY_FRAG_TWEET_ID, id);
usr_favor.putLong(KEY_FRAG_TWEET_ID, id);
usr_tweet.putBoolean(KEY_FRAG_TWEET_FIX, false);
usr_tweet.putBoolean(KEY_FRAG_TWEET_FIX, false);
usr_tweet.putSerializable(KEY_FRAG_TWEET_MODE, TweetType.USER_TWEET);
usr_favor.putSerializable(KEY_FRAG_TWEET_MODE, TweetType.USER_FAVOR);
fragments = new Fragment[2];
fragments[0] = new TweetListFragment();
fragments[1] = new TweetListFragment();
@ -67,12 +71,12 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case SEARCH_TAB:
Bundle tweetSearch = new Bundle();
Bundle userSearch = new Bundle();
tweetSearch.putString("search", search);
userSearch.putString("search", search);
tweetSearch.putSerializable("mode", TweetType.SEARCH);
userSearch.putSerializable("mode", UserType.USEARCH);
tweetSearch.putBoolean("fix", true);
userSearch.putBoolean("fix", true);
tweetSearch.putString(KEY_FRAG_TWEET_SEARCH, search);
userSearch.putString(KEY_FRAG_TWEET_SEARCH, search);
tweetSearch.putSerializable(KEY_FRAG_TWEET_MODE, TweetType.SEARCH);
userSearch.putSerializable(KEY_FRAG_TWEET_MODE, UserType.USEARCH);
tweetSearch.putBoolean(KEY_FRAG_TWEET_FIX, true);
userSearch.putBoolean(KEY_FRAG_TWEET_FIX, true);
fragments = new Fragment[2];
fragments[0] = new TweetListFragment();
fragments[1] = new UserListFragment();
@ -82,10 +86,10 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case TWEET_PAGE:
Bundle param = new Bundle();
param.putSerializable("mode", TweetType.TWEET_ANSR);
param.putString("search", search);
param.putBoolean("fix", false);
param.putLong("id", id);
param.putSerializable(KEY_FRAG_TWEET_MODE, TweetType.TWEET_ANSR);
param.putString(KEY_FRAG_TWEET_SEARCH, search);
param.putBoolean(KEY_FRAG_TWEET_FIX, false);
param.putLong(KEY_FRAG_TWEET_ID, id);
fragments = new Fragment[1];
fragments[0] = new TweetListFragment();
fragments[0].setArguments(param);
@ -98,8 +102,8 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case FRIENDS_PAGE:
Bundle uParam = new Bundle();
uParam.putLong("id", id);
uParam.putSerializable("mode", UserType.FRIENDS);
uParam.putLong(KEY_FRAG_TWEET_ID, id);
uParam.putSerializable(KEY_FRAG_TWEET_MODE, UserType.FRIENDS);
fragments = new Fragment[1];
fragments[0] = new UserListFragment();
fragments[0].setArguments(uParam);
@ -107,8 +111,8 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case FOLLOWER_PAGE:
uParam = new Bundle();
uParam.putLong("id", id);
uParam.putSerializable("mode", UserType.FOLLOWS);
uParam.putLong(KEY_FRAG_TWEET_ID, id);
uParam.putSerializable(KEY_FRAG_TWEET_MODE, UserType.FOLLOWS);
fragments = new Fragment[1];
fragments[0] = new UserListFragment();
fragments[0].setArguments(uParam);
@ -116,8 +120,8 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case RETWEETER_PAGE:
uParam = new Bundle();
uParam.putLong("id", id);
uParam.putSerializable("mode", UserType.RETWEET);
uParam.putLong(KEY_FRAG_TWEET_ID, id);
uParam.putSerializable(KEY_FRAG_TWEET_MODE, UserType.RETWEET);
fragments = new Fragment[1];
fragments[0] = new UserListFragment();
fragments[0].setArguments(uParam);
@ -125,8 +129,8 @@ public class FragmentAdapter extends FragmentPagerAdapter {
case FAVOR_PAGE:
uParam = new Bundle();
uParam.putLong("id", id);
uParam.putSerializable("mode", UserType.FAVORIT);
uParam.putLong(KEY_FRAG_TWEET_ID, id);
uParam.putSerializable(KEY_FRAG_TWEET_MODE, UserType.FAVORIT);
fragments = new Fragment[1];
fragments[0] = new UserListFragment();
fragments[0].setArguments(uParam);

View File

@ -83,16 +83,16 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageHolder>
public interface OnImageClickListener {
/**
* simple click on image
* simple click on image_add
*
* @param image selected image bitmap
* @param image selected image_add bitmap
*/
void onImageClick(Bitmap image);
/**
* long touch on image
* long touch on image_add
*
* @param image selected image bitmap
* @param image selected image_add bitmap
* @return perform onImageClick ?
*/
boolean onImageTouch(Bitmap image);

View File

@ -52,7 +52,7 @@ public abstract class ErrorHandler {
case 179:
case 136:
Toast.makeText(c, R.string.status_private, Toast.LENGTH_SHORT).show();
Toast.makeText(c, R.string.not_authorized, Toast.LENGTH_SHORT).show();
break;
case 186:

View File

@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.ImageAdapter;
import org.nuclearfog.twidda.window.ImageDetail;
import org.nuclearfog.twidda.window.MediaViewer;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@ -22,15 +22,22 @@ import java.net.URL;
public class ImageLoader extends AsyncTask<String, Void, Boolean> {
private WeakReference<ImageDetail> ui;
public enum Mode {
ONLINE,
STORAGE
}
private WeakReference<MediaViewer> ui;
private ImageAdapter imageAdapter;
private Bitmap[] images;
private Mode mode;
public ImageLoader(@NonNull ImageDetail context) {
public ImageLoader(@NonNull MediaViewer context, Mode mode) {
ui = new WeakReference<>(context);
RecyclerView imageList = context.findViewById(R.id.image_list);
imageAdapter = (ImageAdapter) imageList.getAdapter();
this.mode = mode;
}
@ -39,14 +46,17 @@ public class ImageLoader extends AsyncTask<String, Void, Boolean> {
try {
int i = 0;
images = new Bitmap[links.length];
for (String link : links) {
if (link.startsWith("/"))
images[i++] = BitmapFactory.decodeFile(link);
else {
URL u = new URL(link);
InputStream stream = u.openStream();
images[i++] = BitmapFactory.decodeStream(stream);
switch (mode) {
case ONLINE:
URL url = new URL(link);
InputStream stream = url.openStream();
images[i++] = BitmapFactory.decodeStream(stream);
break;
case STORAGE:
images[i++] = BitmapFactory.decodeFile(link);
break;
}
}
} catch (Exception err) {
@ -65,7 +75,7 @@ public class ImageLoader extends AsyncTask<String, Void, Boolean> {
ProgressBar progress = ui.get().findViewById(R.id.image_load);
progress.setVisibility(View.INVISIBLE);
if (success) {
if (success && images.length > 0) {
ui.get().setImage(images[0]);
imageAdapter.setImages(images);
imageAdapter.notifyDataSetChanged();

View File

@ -21,7 +21,7 @@ import com.squareup.picasso.Picasso;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.items.TwitterUser;
import org.nuclearfog.twidda.database.DatabaseAdapter;
import org.nuclearfog.twidda.window.ImageDetail;
import org.nuclearfog.twidda.window.MediaViewer;
import org.nuclearfog.twidda.window.ProfileEdit;
import java.io.File;
@ -29,6 +29,10 @@ import java.lang.ref.WeakReference;
import twitter4j.TwitterException;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_LINK;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.IMAGE;
public class ProfileEditor extends AsyncTask<Void, Void, Void> {
@ -149,9 +153,9 @@ public class ProfileEditor extends AsyncTask<Void, Void, Void> {
pb_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent image = new Intent(ui.get(), ImageDetail.class);
image.putExtra("link", mediaLink);
image.putExtra("storable", false);
Intent image = new Intent(ui.get(), MediaViewer.class);
image.putExtra(KEY_MEDIA_LINK, mediaLink);
image.putExtra(KEY_MEDIA_TYPE, IMAGE);
ui.get().startActivity(image);
}
});

View File

@ -19,7 +19,7 @@ import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.items.TwitterUser;
import org.nuclearfog.twidda.database.DatabaseAdapter;
import org.nuclearfog.twidda.database.GlobalSettings;
import org.nuclearfog.twidda.window.ImageDetail;
import org.nuclearfog.twidda.window.MediaViewer;
import org.nuclearfog.twidda.window.UserProfile;
import java.lang.ref.WeakReference;
@ -31,6 +31,9 @@ import twitter4j.TwitterException;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_LINK;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.IMAGE;
public class ProfileLoader extends AsyncTask<Long, Void, Boolean> {
@ -215,9 +218,9 @@ public class ProfileLoader extends AsyncTask<Long, Void, Boolean> {
profile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent image = new Intent(ui.get(), ImageDetail.class);
image.putExtra("link", new String[]{user.getImageLink()});
image.putExtra("storable", true);
Intent image = new Intent(ui.get(), MediaViewer.class);
image.putExtra(KEY_MEDIA_LINK, new String[]{user.getImageLink()});
image.putExtra(KEY_MEDIA_TYPE, IMAGE);
ui.get().startActivity(image);
}
});

View File

@ -6,6 +6,7 @@ import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
@ -20,6 +21,7 @@ import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.items.Tweet;
import org.nuclearfog.twidda.database.DatabaseAdapter;
import org.nuclearfog.twidda.database.GlobalSettings;
import org.nuclearfog.twidda.window.MediaViewer;
import org.nuclearfog.twidda.window.TweetDetail;
import org.nuclearfog.twidda.window.UserProfile;
@ -30,6 +32,13 @@ import java.text.SimpleDateFormat;
import twitter4j.TwitterException;
import static android.view.View.VISIBLE;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_LINK;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.ANGIF;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.IMAGE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.VIDEO;
import static org.nuclearfog.twidda.window.TweetDetail.KEY_TWEET_ID;
import static org.nuclearfog.twidda.window.TweetDetail.KEY_TWEET_NAME;
import static org.nuclearfog.twidda.window.TweetDetail.STAT_CHANGED;
@ -167,22 +176,53 @@ public class StatusLoader extends AsyncTask<Long, Void, Void> {
} else {
scrName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
if (tweet.getMediaLinks() != null && tweet.getMediaLinks().length > 0) {
View mediaButton = ui.get().findViewById(R.id.image_attach);
mediaButton.setVisibility(VISIBLE);
mediaButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ui.get().imageClick(tweet.getMediaLinks());
}
});
if (tweet.hasMedia()) {
String link = tweet.getMediaLinks()[0];
if (link.contains(".jpg")) { // Image
View imageButton = ui.get().findViewById(R.id.image_attach);
imageButton.setVisibility(VISIBLE);
imageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent media = new Intent(ui.get(), MediaViewer.class);
media.putExtra(KEY_MEDIA_LINK, tweet.getMediaLinks());
media.putExtra(KEY_MEDIA_TYPE, IMAGE);
ui.get().startActivity(media);
}
});
} else if (link.contains(".mp4")) { // GIF
View videoButton = ui.get().findViewById(R.id.video_attach);
videoButton.setVisibility(VISIBLE);
videoButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent media = new Intent(ui.get(), MediaViewer.class);
media.putExtra(KEY_MEDIA_LINK, tweet.getMediaLinks());
media.putExtra(KEY_MEDIA_TYPE, ANGIF);
ui.get().startActivity(media);
}
});
} else if (link.contains(".m3u8")) { // video stream
View videoButton = ui.get().findViewById(R.id.video_attach);
videoButton.setVisibility(VISIBLE);
videoButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent media = new Intent(ui.get(), MediaViewer.class);
media.putExtra(KEY_MEDIA_LINK, tweet.getMediaLinks());
media.putExtra(KEY_MEDIA_TYPE, VIDEO);
ui.get().startActivity(media);
}
});
}
}
profile_img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent profile = new Intent(ui.get(), UserProfile.class);
profile.putExtra("userID", tweet.getUser().getId());
profile.putExtra("username", tweet.getUser().getScreenname());
profile.putExtra(UserProfile.KEY_PROFILE_ID, tweet.getUser().getId());
profile.putExtra(UserProfile.KEY_PROFILE_NAME, tweet.getUser().getScreenname());
ui.get().startActivity(profile);
}
});
@ -209,8 +249,8 @@ public class StatusLoader extends AsyncTask<Long, Void, Void> {
@Override
public void onClick(View v) {
Intent intent = new Intent(ui.get(), TweetDetail.class);
intent.putExtra("tweetID", tweet.getReplyId());
intent.putExtra("username", tweet.getReplyName());
intent.putExtra(KEY_TWEET_ID, tweet.getReplyId());
intent.putExtra(KEY_TWEET_NAME, tweet.getReplyName());
ui.get().startActivity(intent);
}
});

View File

@ -14,29 +14,28 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.items.TweetHolder;
import org.nuclearfog.twidda.window.TweetPopup;
import java.lang.ref.WeakReference;
import twitter4j.TwitterException;
public class StatusUploader extends AsyncTask<String, Void, Boolean> {
public class StatusUploader extends AsyncTask<Void, Void, Boolean> {
private WeakReference<TweetPopup> ui;
private TwitterEngine mTwitter;
private TwitterException err;
private LayoutInflater inflater;
private Dialog popup;
private String tweet;
private long replyId;
private TweetHolder tweet;
public StatusUploader(@NonNull TweetPopup context, String tweet, long replyId) {
public StatusUploader(@NonNull TweetPopup context, TweetHolder tweet) {
ui = new WeakReference<>(context);
mTwitter = TwitterEngine.getInstance(context);
inflater = LayoutInflater.from(context);
popup = new Dialog(context);
this.replyId = replyId;
this.tweet = tweet;
}
@ -71,13 +70,9 @@ public class StatusUploader extends AsyncTask<String, Void, Boolean> {
@Override
protected Boolean doInBackground(String... path) {
protected Boolean doInBackground(Void[] v) {
try {
if (path.length == 0) {
mTwitter.sendStatus(tweet, replyId, null);
} else {
mTwitter.sendStatus(tweet, replyId, path);
}
mTwitter.uploadStatus(tweet);
} catch (TwitterException err) {
this.err = err;
return false;
@ -119,4 +114,6 @@ public class StatusUploader extends AsyncTask<String, Void, Boolean> {
protected void onCancelled() {
popup.dismiss();
}
}

View File

@ -8,10 +8,13 @@ import org.nuclearfog.twidda.BuildConfig;
import org.nuclearfog.twidda.backend.items.Message;
import org.nuclearfog.twidda.backend.items.Trend;
import org.nuclearfog.twidda.backend.items.Tweet;
import org.nuclearfog.twidda.backend.items.TweetHolder;
import org.nuclearfog.twidda.backend.items.TwitterUser;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.List;
@ -400,29 +403,23 @@ public class TwitterEngine {
/**
* Send Tweet
*
* @param text Tweet Text
* @param reply In reply to tweet ID
* @param path Path to the Media File
* @throws TwitterException if Access is unavailable
* send tweet
* @param tweet Tweet holder
* @throws TwitterException if twitter service is unavailable
* @throws FileNotFoundException if file was not found
*/
public void sendStatus(String text, long reply, @Nullable String[] path) throws TwitterException {
StatusUpdate mStatus = new StatusUpdate(text);
if (reply > 0)
mStatus.setInReplyToStatusId(reply);
if (path != null) {
final int count = path.length;
long[] mIDs = new long[count];
for (int i = 0; i < count; i++) {
String current = path[i];
UploadedMedia media = twitter.uploadMedia(new File(current));
mIDs[i] = media.getMediaId();
}
mStatus.setMediaIds(mIDs);
public void uploadStatus(TweetHolder tweet) throws TwitterException, FileNotFoundException {
StatusUpdate mStatus = new StatusUpdate(tweet.getText());
if (tweet.isReply())
mStatus.setInReplyToStatusId(tweet.getReplyId());
if (tweet.hasImages()) {
long[] ids = uploadImages(tweet.getImageLink());
mStatus.setMediaIds(ids);
} else if (tweet.hasVideo()) {
long[] ids = uploadVideo(tweet.getVideoLink());
mStatus.setMediaIds(ids);
}
twitter.tweets().updateStatus(mStatus);
twitter.updateStatus(mStatus);
}
@ -591,9 +588,9 @@ public class TwitterEngine {
/**
* Update user profile image
* Update user profile image_add
*
* @param image image file
* @param image image_add file
* @throws TwitterException if Access is unavailable
*/
public void updateProfileImage(File image) throws TwitterException {
@ -640,4 +637,39 @@ public class TwitterEngine {
User receiver = twitter.showUser(dm.getRecipientId());
return new Message(dm, sender, receiver);
}
/**
* Upload image to twitter and return unique media IDs
*
* @param paths Image Paths
* @return Media ID array
* @throws TwitterException if twitter service is unavailable
* @throws FileNotFoundException if file was not found
*/
private long[] uploadImages(String[] paths) throws TwitterException, FileNotFoundException {
long[] ids = new long[paths.length];
int i = 0;
for (String path : paths) {
File file = new File(path);
UploadedMedia media = twitter.uploadMedia(file.getName(), new FileInputStream(file));
ids[i++] = media.getMediaId();
}
return ids;
}
/**
* Upload video or gif to twitter and return unique media ID
*
* @param path path of video or gif
* @return media ID
* @throws TwitterException if twitter service is unavailable
* @throws FileNotFoundException if file was not found
*/
private long[] uploadVideo(String path) throws TwitterException, FileNotFoundException {
File file = new File(path);
UploadedMedia media = twitter.uploadMediaChunked(file.getName(), new FileInputStream(file));
return new long[]{media.getMediaId()};
}
}

View File

@ -8,10 +8,14 @@ import twitter4j.URLEntity;
public class Tweet {
private static final String PHOTO = "photo";
private static final String VIDEO = "video";
private static final String ANGIF = "animated_gif";
private final long tweetID;
private final long time;
private final String tweet;
private final String[] media;
private final String[] medias;
private final String source;
private final TwitterUser user;
@ -37,7 +41,7 @@ public class Tweet {
time = status.getCreatedAt().getTime();
replyID = status.getInReplyToStatusId();
replyName = '@' + status.getInReplyToScreenName();
media = getMediaLinks(status);
medias = getMediaLinks(status);
retweeted = status.isRetweeted();
favored = status.isFavorited();
myRetweetId = status.getCurrentUserRetweetId();
@ -56,7 +60,7 @@ public class Tweet {
public Tweet(long tweetID, int retweetCount, int favoriteCount, TwitterUser user, String tweet, long time,
String replyName, long replyUserId, String[] media, String source, long replyID,
String replyName, long replyUserId, String[] medias, String source, long replyID,
Tweet embedded, long myRetweetId, boolean retweeted, boolean favored) {
this.tweetID = tweetID;
this.user = user;
@ -67,7 +71,7 @@ public class Tweet {
this.replyID = replyID;
this.embedded = embedded;
this.replyName = replyName;
this.media = media;
this.medias = medias;
this.source = source;
this.retweeted = retweeted;
this.favored = favored;
@ -185,12 +189,21 @@ public class Tweet {
}
/**
* get media links of tweet
* get medias links of tweet
*
* @return media links array
* @return medias links array
*/
public String[] getMediaLinks() {
return media;
return medias;
}
/**
* check if tweet contains media
*
* @return true if tweet contains media
*/
public boolean hasMedia() {
return medias != null && medias.length > 0;
}
/**
@ -218,8 +231,25 @@ public class Tweet {
private String[] getMediaLinks(Status status) {
MediaEntity[] mediaEntities = status.getMediaEntities();
String[] medias = new String[mediaEntities.length];
for (int i = 0; i < medias.length; i++)
medias[i] = mediaEntities[i].getMediaURLHttps();
for (int i = 0; i < medias.length; i++) {
MediaEntity mediaEntity = mediaEntities[i];
switch (mediaEntity.getType()) {
case PHOTO:
medias[i] = mediaEntity.getMediaURLHttps();
break;
case VIDEO:
for (MediaEntity.Variant type : mediaEntity.getVideoVariants()) {
if (type.getContentType().equals("application/x-mpegURL"))
medias[i] = type.getUrl();
}
break;
case ANGIF:
medias[i] = mediaEntity.getVideoVariants()[0].getUrl();
break;
}
}
return medias;
}

View File

@ -0,0 +1,62 @@
package org.nuclearfog.twidda.backend.items;
import androidx.annotation.NonNull;
public class TweetHolder {
private final String text;
private final long replyId;
private final String[] imageLink;
private final String videoLink;
public TweetHolder(String text, long replyId) {
this.text = text;
this.replyId = replyId;
imageLink = new String[0];
videoLink = "";
}
public TweetHolder(String text, long replyId, @NonNull String[] mediaLinks) {
this.text = text;
this.replyId = replyId;
String extension = mediaLinks[0];
extension = extension.substring(extension.lastIndexOf('.'));
if (extension.equals(".jpg") || extension.equals(".png")) {
videoLink = "";
imageLink = mediaLinks;
} else {
imageLink = new String[0];
videoLink = mediaLinks[0];
}
}
public String getText() {
return text;
}
public long getReplyId() {
return replyId;
}
public String getVideoLink() {
return videoLink;
}
public String[] getImageLink() {
return imageLink;
}
public boolean hasImages() {
return imageLink.length > 0;
}
public boolean hasVideo() {
return !videoLink.isEmpty();
}
public boolean isReply() {
return replyId > 0;
}
}

View File

@ -31,7 +31,7 @@ public class TwitterUser {
userID = user.getId();
username = user.getName();
screenname = '@' + user.getScreenName();
profileImg = user.getOriginalProfileImageURL();
profileImg = user.getOriginalProfileImageURLHttps();
bio = getBio(user);
link = user.getURLEntity().getExpandedURL();
location = user.getLocation();
@ -105,7 +105,7 @@ public class TwitterUser {
}
/**
* get Profile image link
* get Profile image_add link
*
* @return link
*/
@ -114,7 +114,7 @@ public class TwitterUser {
}
/**
* get banner image link
* get banner image_add link
*
* @return link
*/

View File

@ -6,6 +6,9 @@ import android.database.sqlite.SQLiteOpenHelper;
public class AppDatabase extends SQLiteOpenHelper {
public static final String DB_NAME = "database.db";
private static final String userTable = "CREATE TABLE IF NOT EXISTS user (" +
"userID INTEGER PRIMARY KEY,username VARCHAR(50),scrname VARCHAR(15)," +
"pbLink TEXT,banner TEXT,bio TEXT,location TEXT,link TEXT,userregister INTEGER," +
@ -34,7 +37,7 @@ public class AppDatabase extends SQLiteOpenHelper {
public AppDatabase(Context context) {
super(context, "database.db", null, 4);
super(context, DB_NAME, null, 4);
}

View File

@ -149,7 +149,7 @@ public class GlobalSettings {
}
/**
* image loading enabled
* image_add loading enabled
*
* @return true if enabled
*/
@ -158,7 +158,7 @@ public class GlobalSettings {
}
/**
* enable/disable image load
* enable/disable image_add load
*
* @param image true if enabled
*/

View File

@ -1,7 +1,6 @@
package org.nuclearfog.twidda.fragment;
import android.content.Intent;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -25,6 +24,8 @@ import org.nuclearfog.twidda.window.MessagePopup;
import org.nuclearfog.twidda.window.SearchPage;
import org.nuclearfog.twidda.window.UserProfile;
import static android.os.AsyncTask.Status.RUNNING;
public class MessageListFragment extends Fragment implements OnRefreshListener, OnItemSelected {
@ -71,7 +72,7 @@ public class MessageListFragment extends Fragment implements OnRefreshListener,
@Override
public void onStop() {
super.onStop();
if (messageTask != null && messageTask.getStatus() == Status.RUNNING)
if (messageTask != null && messageTask.getStatus() == RUNNING)
messageTask.cancel(true);
}

View File

@ -1,7 +1,6 @@
package org.nuclearfog.twidda.fragment;
import android.content.Intent;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -23,6 +22,8 @@ import org.nuclearfog.twidda.fragment.backend.TrendLoader;
import org.nuclearfog.twidda.fragment.backend.TrendLoader.Mode;
import org.nuclearfog.twidda.window.SearchPage;
import static android.os.AsyncTask.Status.RUNNING;
public class TrendListFragment extends Fragment implements OnRefreshListener, OnItemClickListener, OnStateChange {
@ -69,7 +70,7 @@ public class TrendListFragment extends Fragment implements OnRefreshListener, On
@Override
public void onStop() {
if (trendTask != null && trendTask.getStatus() == Status.RUNNING)
if (trendTask != null && trendTask.getStatus() == RUNNING)
trendTask.cancel(true);
super.onStop();
}

View File

@ -1,7 +1,6 @@
package org.nuclearfog.twidda.fragment;
import android.content.Intent;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -25,9 +24,15 @@ import org.nuclearfog.twidda.fragment.backend.TweetLoader;
import org.nuclearfog.twidda.fragment.backend.TweetLoader.Mode;
import org.nuclearfog.twidda.window.TweetDetail;
import static android.os.AsyncTask.Status.RUNNING;
public class TweetListFragment extends Fragment implements OnRefreshListener, OnItemClickListener, OnStateChange {
public static final String KEY_FRAG_TWEET_MODE = "mode";
public static final String KEY_FRAG_TWEET_SEARCH = "search";
public static final String KEY_FRAG_TWEET_ID = "ID";
public static final String KEY_FRAG_TWEET_FIX = "fix";
public enum TweetType {
HOME,
MENT,
@ -52,11 +57,11 @@ public class TweetListFragment extends Fragment implements OnRefreshListener, On
super.onCreateView(inflater, parent, param);
boolean fixSize;
Bundle b = getArguments();
if (b != null && b.containsKey("mode")) {
mode = (TweetType) b.getSerializable("mode");
id = b.getLong("id", -1);
search = b.getString("search", "");
fixSize = b.getBoolean("fix", false);
if (b != null && b.containsKey(KEY_FRAG_TWEET_MODE)) {
mode = (TweetType) b.getSerializable(KEY_FRAG_TWEET_MODE);
id = b.getLong(KEY_FRAG_TWEET_ID, -1);
search = b.getString(KEY_FRAG_TWEET_SEARCH, "");
fixSize = b.getBoolean(KEY_FRAG_TWEET_FIX, false);
} else {
throw new AssertionError();
}
@ -123,7 +128,7 @@ public class TweetListFragment extends Fragment implements OnRefreshListener, On
@Override
public void onStop() {
if (tweetTask != null && tweetTask.getStatus() == Status.RUNNING)
if (tweetTask != null && tweetTask.getStatus() == RUNNING)
tweetTask.cancel(true);
super.onStop();
}
@ -136,26 +141,32 @@ public class TweetListFragment extends Fragment implements OnRefreshListener, On
tweetTask = new TweetLoader(root, Mode.TL_HOME);
tweetTask.execute();
break;
case MENT:
tweetTask = new TweetLoader(root, Mode.TL_MENT);
tweetTask.execute();
break;
case USER_TWEET:
tweetTask = new TweetLoader(root, Mode.USR_TWEETS);
tweetTask.execute(id);
break;
case USER_FAVOR:
tweetTask = new TweetLoader(root, Mode.USR_FAVORS);
tweetTask.execute(id);
break;
case TWEET_ANSR:
tweetTask = new TweetLoader(root, Mode.TWEET_ANS);
tweetTask.execute(id, search);
break;
case SEARCH:
tweetTask = new TweetLoader(root, Mode.TWEET_SEARCH);
tweetTask.execute(search);
break;
default:
if (BuildConfig.DEBUG)
throw new AssertionError("mode failure");
@ -170,10 +181,10 @@ public class TweetListFragment extends Fragment implements OnRefreshListener, On
Tweet tweet = adapter.getData(pos);
if (tweet.getEmbeddedTweet() != null)
tweet = tweet.getEmbeddedTweet();
Intent intent = new Intent(getContext(), TweetDetail.class);
intent.putExtra("tweetID", tweet.getId());
intent.putExtra("username", tweet.getUser().getScreenname());
startActivity(intent);
Intent tweetIntent = new Intent(getContext(), TweetDetail.class);
tweetIntent.putExtra("tweetID", tweet.getId());
tweetIntent.putExtra("username", tweet.getUser().getScreenname());
startActivity(tweetIntent);
}
}

View File

@ -1,7 +1,6 @@
package org.nuclearfog.twidda.fragment;
import android.content.Intent;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -24,9 +23,16 @@ import org.nuclearfog.twidda.fragment.backend.UserLoader;
import org.nuclearfog.twidda.fragment.backend.UserLoader.Mode;
import org.nuclearfog.twidda.window.UserProfile;
import static android.os.AsyncTask.Status.RUNNING;
public class UserListFragment extends Fragment implements OnRefreshListener, OnItemClickListener {
public static final String KEY_FRAG_USER_MODE = "mode";
public static final String KEY_FRAG_USER_SEARCH = "search";
public static final String KEY_FRAG_USER_ID = "ID";
public static final String KEY_FRAG_USER_FIX = "fix";
public enum UserType {
FOLLOWS,
FRIENDS,
@ -47,11 +53,11 @@ public class UserListFragment extends Fragment implements OnRefreshListener, OnI
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle param) {
Bundle b = getArguments();
if (b != null && b.containsKey("mode")) {
mode = (UserType) b.getSerializable("mode");
id = b.getLong("id", -1);
search = b.getString("search", "");
fixLayout = b.getBoolean("fix", true);
if (b != null && b.containsKey(KEY_FRAG_USER_MODE)) {
mode = (UserType) b.getSerializable(KEY_FRAG_USER_MODE);
id = b.getLong(KEY_FRAG_USER_ID, -1);
search = b.getString(KEY_FRAG_USER_SEARCH, "");
fixLayout = b.getBoolean(KEY_FRAG_USER_FIX, true);
} else if (BuildConfig.DEBUG) {
throw new AssertionError("Bundle error!");
}
@ -90,7 +96,7 @@ public class UserListFragment extends Fragment implements OnRefreshListener, OnI
@Override
public void onStop() {
if (userTask != null && userTask.getStatus() == Status.RUNNING)
if (userTask != null && userTask.getStatus() == RUNNING)
userTask.cancel(true);
super.onStop();
}

View File

@ -33,18 +33,22 @@ import org.nuclearfog.twidda.adapter.WorldIdAdapter;
import org.nuclearfog.twidda.backend.TwitterEngine;
import org.nuclearfog.twidda.database.GlobalSettings;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static org.nuclearfog.twidda.database.AppDatabase.DB_NAME;
public class AppSettings extends AppCompatActivity implements OnClickListener,
OnDismissListener, OnItemSelectedListener, OnCheckedChangeListener {
private static final int[][] checkboxStates = new int[][]{{android.R.attr.state_checked}, {}};
private final int[] checkboxColor = new int[]{0, Color.WHITE};
private static final int BACKGROUND = 0;
private static final int FONTCOLOR = 1;
private static final int HIGHLIGHT = 2;
private static final int POPUPCOLOR = 3;
private static final int[][] checkboxStates = new int[][]{{android.R.attr.state_checked}, {}};
private final int[] checkboxColor = new int[]{0, Color.WHITE};
private GlobalSettings settings;
private Button colorButton1, colorButton2, colorButton3, colorButton4;
private CheckBox toggleImg, toggleAns;
@ -110,7 +114,7 @@ public class AppSettings extends AppCompatActivity implements OnClickListener,
link.setLinkTextColor(settings.getHighlightColor());
if (settings.getCustomWidSet()) {
String text = Long.toString(settings.getWoeId());
woeIdText.setVisibility(View.VISIBLE);
woeIdText.setVisibility(VISIBLE);
woeIdText.setText(text);
}
toggleImg.setOnCheckedChangeListener(this);
@ -141,7 +145,7 @@ public class AppSettings extends AppCompatActivity implements OnClickListener,
.setPositiveButton(R.string.yes_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteDatabase("database.db");
deleteDatabase(DB_NAME);
}
})
.show();
@ -156,7 +160,7 @@ public class AppSettings extends AppCompatActivity implements OnClickListener,
public void onClick(DialogInterface dialog, int which) {
settings.logout();
TwitterEngine.destroyInstance();
deleteDatabase("database.db");
deleteDatabase(DB_NAME);
finish();
}
})
@ -243,11 +247,11 @@ public class AppSettings extends AppCompatActivity implements OnClickListener,
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (position == parent.getCount() - 1) {
woeIdText.setVisibility(View.VISIBLE);
woeIdText.setVisibility(VISIBLE);
settings.setCustomWidSet(true);
settings.setWoeId(1);
} else {
woeIdText.setVisibility(View.INVISIBLE);
woeIdText.setVisibility(INVISIBLE);
woeIdText.setText("");
settings.setCustomWidSet(false);
settings.setWoeId(id);

View File

@ -12,18 +12,17 @@ import androidx.viewpager.widget.ViewPager;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.FragmentAdapter;
import org.nuclearfog.twidda.adapter.FragmentAdapter.AdapterType;
import org.nuclearfog.twidda.database.GlobalSettings;
import static org.nuclearfog.twidda.adapter.FragmentAdapter.AdapterType.MESSAGE_PAGE;
public class DirectMessage extends AppCompatActivity {
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.page_dm);
Toolbar tool = findViewById(R.id.dm_toolbar);
View root = findViewById(R.id.dm_layout);
ViewPager pager = findViewById(R.id.dm_pager);
@ -32,7 +31,7 @@ public class DirectMessage extends AppCompatActivity {
if (getSupportActionBar() != null)
getSupportActionBar().setTitle(R.string.directmessage);
FragmentAdapter adapter = new FragmentAdapter(getSupportFragmentManager(), AdapterType.MESSAGE_PAGE, 0, "");
FragmentAdapter adapter = new FragmentAdapter(getSupportFragmentManager(), MESSAGE_PAGE, 0, "");
pager.setOffscreenPageLimit(1);
pager.setAdapter(adapter);

View File

@ -1,137 +0,0 @@
package org.nuclearfog.twidda.window;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.AsyncTask.Status;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.Display;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.BuildConfig;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.ImageAdapter;
import org.nuclearfog.twidda.adapter.ImageAdapter.OnImageClickListener;
import org.nuclearfog.twidda.backend.ImageLoader;
import org.nuclearfog.zoomview.ZoomView;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL;
public class ImageDetail extends AppCompatActivity implements OnImageClickListener {
boolean storable;
private ImageLoader imageAsync;
private ZoomView zoomImage;
private String[] link;
private int width;
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.page_image);
Bundle param = getIntent().getExtras();
if (param != null && param.containsKey("link") && param.containsKey("storable")) {
link = param.getStringArray("link");
storable = param.getBoolean("storable");
} else if (BuildConfig.DEBUG)
throw new AssertionError();
zoomImage = findViewById(R.id.image_full);
RecyclerView imageList = findViewById(R.id.image_list);
imageList.setLayoutManager(new LinearLayoutManager(this, HORIZONTAL, false));
imageList.setAdapter(new ImageAdapter(this));
Display d = getWindowManager().getDefaultDisplay();
Point size = new Point();
d.getSize(size);
width = size.x;
}
@Override
protected void onStart() {
super.onStart();
if (imageAsync == null) {
imageAsync = new ImageLoader(this);
imageAsync.execute(link);
}
}
@Override
protected void onDestroy() {
if (imageAsync != null && imageAsync.getStatus() == Status.RUNNING)
imageAsync.cancel(true);
super.onDestroy();
}
@Override
public void onImageClick(Bitmap image) {
setImage(image);
}
@Override
public boolean onImageTouch(Bitmap image) {
if (storable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int check = checkSelfPermission(WRITE_EXTERNAL_STORAGE);
if (check == PERMISSION_GRANTED) {
storeImage(image);
} else {
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 1);
}
} else {
storeImage(image);
}
return true;
}
return false;
}
public void setImage(Bitmap image) {
float ratio = image.getWidth() / (float) width;
int destHeight = (int) (image.getHeight() / ratio);
image = Bitmap.createScaledBitmap(image, width, destHeight, false);
zoomImage.reset();
zoomImage.setImageBitmap(image);
}
private void storeImage(Bitmap image) {
String path = Environment.getExternalStorageDirectory().toString();
path += "/Pictures/Shitter";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.GERMANY);
String name = "shitter_" + formatter.format(new Date()) + ".png";
File dir = new File(path);
if (dir.mkdirs())
Toast.makeText(this, R.string.image_folder_created, Toast.LENGTH_SHORT).show();
File file = new File(dir, name);
try {
FileOutputStream output = new FileOutputStream(file);
image.compress(Bitmap.CompressFormat.PNG, 0, output);
Toast.makeText(this, R.string.image_saved, Toast.LENGTH_LONG).show();
output.close();
} catch (Exception err) {
Toast.makeText(this, R.string.image_store_failure, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -1,11 +1,8 @@
package org.nuclearfog.twidda.window;
import android.content.Context;
import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
@ -18,6 +15,11 @@ import androidx.appcompat.app.AppCompatActivity;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.Registration;
import static android.content.Intent.ACTION_VIEW;
import static android.os.AsyncTask.Status.RUNNING;
import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;
public class LoginPage extends AppCompatActivity implements OnClickListener {
@ -31,8 +33,6 @@ public class LoginPage extends AppCompatActivity implements OnClickListener {
pin = findViewById(R.id.pin);
Button btnLink = findViewById(R.id.linkButton);
Button btnVeri = findViewById(R.id.verifier);
btnLink.getBackground().setColorFilter(new LightingColorFilter(0xffffffff, 0xff00aa));
btnVeri.getBackground().setColorFilter(new LightingColorFilter(0xffffffff, 0xff00aa));
btnLink.setOnClickListener(this);
btnVeri.setOnClickListener(this);
}
@ -40,7 +40,7 @@ public class LoginPage extends AppCompatActivity implements OnClickListener {
@Override
protected void onDestroy() {
if (registerAsync != null && registerAsync.getStatus() == Status.RUNNING)
if (registerAsync != null && registerAsync.getStatus() == RUNNING)
registerAsync.cancel(true);
super.onDestroy();
}
@ -55,7 +55,7 @@ public class LoginPage extends AppCompatActivity implements OnClickListener {
@Override
public void onClick(View v) {
if (registerAsync != null && registerAsync.getStatus() == Status.RUNNING)
if (registerAsync != null && registerAsync.getStatus() == RUNNING)
registerAsync.cancel(true);
switch (v.getId()) {
@ -70,7 +70,7 @@ public class LoginPage extends AppCompatActivity implements OnClickListener {
registerAsync = new Registration(this);
registerAsync.execute(twitterPin);
} else {
Toast.makeText(this, R.string.enter_pin, Toast.LENGTH_LONG).show();
Toast.makeText(this, R.string.enter_pin, LENGTH_LONG).show();
}
break;
}
@ -78,15 +78,15 @@ public class LoginPage extends AppCompatActivity implements OnClickListener {
public void connect(String link) {
ConnectivityManager mConnect = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
ConnectivityManager mConnect = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
if (mConnect != null && mConnect.getActiveNetworkInfo() != null) {
if (mConnect.getActiveNetworkInfo().isConnected()) {
Intent browser = new Intent(Intent.ACTION_VIEW);
Intent browser = new Intent(ACTION_VIEW);
browser.setData(Uri.parse(link));
startActivity(browser);
}
} else {
Toast.makeText(this, R.string.connection_failed, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.connection_failed, LENGTH_SHORT).show();
}
}
}

View File

@ -0,0 +1,233 @@
package org.nuclearfog.twidda.window;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.AsyncTask.Status;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.Display;
import android.view.View;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.BuildConfig;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.ImageAdapter;
import org.nuclearfog.twidda.adapter.ImageAdapter.OnImageClickListener;
import org.nuclearfog.twidda.backend.ImageLoader;
import org.nuclearfog.zoomview.ZoomView;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.View.VISIBLE;
import static androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL;
import static org.nuclearfog.twidda.backend.ImageLoader.Mode.ONLINE;
import static org.nuclearfog.twidda.backend.ImageLoader.Mode.STORAGE;
public class MediaViewer extends AppCompatActivity implements OnImageClickListener, OnPreparedListener {
public static final String KEY_MEDIA_LINK = "link";
public static final String KEY_MEDIA_TYPE = "mediatype";
public enum MediaType {
IMAGE,
IMAGE_STORAGE,
VIDEO,
VIDEO_STORAGE,
ANGIF,
ANGIF_STORAGE
}
private ImageLoader imageAsync;
private VideoView videoView;
private ZoomView zoomImage;
private MediaType type;
private String[] link;
private int width;
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.page_image);
RecyclerView imageList = findViewById(R.id.image_list);
MediaController videoController = findViewById(R.id.video_controller);
View imageWindow = findViewById(R.id.image_window);
View videoWindow = findViewById(R.id.video_window);
zoomImage = findViewById(R.id.image_full);
videoView = findViewById(R.id.video_view);
Bundle param = getIntent().getExtras();
if (param != null && param.containsKey(KEY_MEDIA_LINK) && param.containsKey(KEY_MEDIA_TYPE)) {
link = param.getStringArray(KEY_MEDIA_LINK);
type = (MediaType) param.getSerializable(KEY_MEDIA_TYPE);
} else if (BuildConfig.DEBUG)
throw new AssertionError();
switch (type) {
case IMAGE:
case IMAGE_STORAGE:
imageList.setLayoutManager(new LinearLayoutManager(this, HORIZONTAL, false));
imageList.setAdapter(new ImageAdapter(this));
Display d = getWindowManager().getDefaultDisplay();
Point size = new Point();
d.getSize(size);
width = size.x;
imageWindow.setVisibility(VISIBLE);
break;
case ANGIF:
videoController.hide();
videoWindow.setVisibility(VISIBLE);
videoView.setVideoPath(link[0]);
videoView.setOnPreparedListener(this);
break;
case ANGIF_STORAGE:
videoController.hide();
videoWindow.setVisibility(VISIBLE);
videoView.setVideoPath(link[0]);
videoView.setOnPreparedListener(this);
break;
case VIDEO:
videoController.setAnchorView(videoView);
videoView.setMediaController(videoController);
videoWindow.setVisibility(VISIBLE);
videoView.setVideoURI(Uri.parse(link[0]));
break;
case VIDEO_STORAGE:
videoController.setAnchorView(videoView);
videoView.setMediaController(videoController);
videoWindow.setVisibility(VISIBLE);
videoView.setVideoPath(link[0]);
break;
}
}
@Override
protected void onStart() {
super.onStart();
switch (type) {
case IMAGE:
if (imageAsync == null) {
imageAsync = new ImageLoader(this, ONLINE);
imageAsync.execute(link);
}
case IMAGE_STORAGE:
if (imageAsync == null) {
imageAsync = new ImageLoader(this, STORAGE);
imageAsync.execute(link);
}
break;
case VIDEO:
case ANGIF:
case VIDEO_STORAGE:
case ANGIF_STORAGE:
if (videoView.getCurrentPosition() == 0)
videoView.start();
else
videoView.resume();
break;
}
}
@Override
protected void onPause() {
super.onPause();
if (type != MediaType.IMAGE && type != MediaType.IMAGE_STORAGE)
videoView.pause();
}
@Override
protected void onDestroy() {
if (imageAsync != null && imageAsync.getStatus() == Status.RUNNING)
imageAsync.cancel(true);
super.onDestroy();
}
@Override
public void onImageClick(Bitmap image) {
setImage(image);
}
@Override
public boolean onImageTouch(Bitmap image) {
if (type == MediaType.IMAGE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int check = checkSelfPermission(WRITE_EXTERNAL_STORAGE);
if (check == PERMISSION_GRANTED) {
storeImage(image);
} else {
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 1);
}
} else {
storeImage(image);
}
return true;
}
return false;
}
@Override
public void onPrepared(MediaPlayer mp) {
if (type == MediaType.ANGIF || type == MediaType.ANGIF_STORAGE)
mp.setLooping(true);
}
public void setImage(Bitmap image) {
float ratio = image.getWidth() / (float) width;
int destHeight = (int) (image.getHeight() / ratio);
image = Bitmap.createScaledBitmap(image, width, destHeight, false);
zoomImage.reset();
zoomImage.setImageBitmap(image);
}
private void storeImage(Bitmap image) {
String path = Environment.getExternalStorageDirectory().toString();
path += "/Pictures/Shitter";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.GERMANY);
String name = "shitter_" + formatter.format(new Date()) + ".png";
File dir = new File(path);
if (dir.mkdirs())
Toast.makeText(this, R.string.image_folder_created, Toast.LENGTH_SHORT).show();
File file = new File(dir, name);
try {
FileOutputStream output = new FileOutputStream(file);
image.compress(Bitmap.CompressFormat.PNG, 0, output);
Toast.makeText(this, R.string.image_saved, Toast.LENGTH_LONG).show();
output.close();
} catch (Exception err) {
Toast.makeText(this, R.string.image_store_failure, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -3,9 +3,7 @@ package org.nuclearfog.twidda.window;
import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask.Status;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
@ -22,11 +20,24 @@ import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.MessageUpload;
import org.nuclearfog.twidda.database.GlobalSettings;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.Intent.ACTION_PICK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.AsyncTask.Status.RUNNING;
import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_LINK;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.IMAGE_STORAGE;
public class MessagePopup extends AppCompatActivity implements OnClickListener {
public static final String KEY_DM_ADDITION = "addition";
private static final String[] PERM_READ = {Manifest.permission.READ_EXTERNAL_STORAGE};
private static final String[] PICK_IMAGE = {MediaStore.Images.Media.DATA};
private static final int REQ_PERM_READ = 4;
private MessageUpload messageAsync;
private EditText receiver, text;
private String mediaPath = "";
@ -36,10 +47,10 @@ public class MessagePopup extends AppCompatActivity implements OnClickListener {
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.popup_dm);
String username = "";
String addtion = "";
Bundle param = getIntent().getExtras();
if (param != null) {
username = param.getString("username");
addtion = param.getString(KEY_DM_ADDITION, "");
}
View root = findViewById(R.id.dm_popup);
@ -51,7 +62,7 @@ public class MessagePopup extends AppCompatActivity implements OnClickListener {
GlobalSettings settings = GlobalSettings.getInstance(this);
root.setBackgroundColor(settings.getPopupColor());
receiver.append(username);
receiver.append(addtion);
send.setOnClickListener(this);
media.setOnClickListener(this);
}
@ -68,7 +79,7 @@ public class MessagePopup extends AppCompatActivity implements OnClickListener {
closeDialog.setPositiveButton(R.string.yes_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (messageAsync != null && messageAsync.getStatus() == Status.RUNNING)
if (messageAsync != null && messageAsync.getStatus() == RUNNING)
messageAsync.cancel(true);
finish();
}
@ -81,13 +92,14 @@ public class MessagePopup extends AppCompatActivity implements OnClickListener {
@Override
protected void onActivityResult(int reqCode, int returnCode, Intent i) {
super.onActivityResult(reqCode, returnCode, i);
if (returnCode == RESULT_OK && i.getData() != null) {
String[] mode = {MediaStore.Images.Media.DATA};
Cursor c = getContentResolver().query(i.getData(), mode, null, null, null);
if (c != null && c.moveToFirst()) {
int index = c.getColumnIndex(mode[0]);
mediaPath = c.getString(index);
c.close();
if (i.getData() != null) {
if (reqCode == REQ_PERM_READ && returnCode == RESULT_OK) {
Cursor c = getContentResolver().query(i.getData(), PICK_IMAGE, null, null, null);
if (c != null && c.moveToFirst()) {
int index = c.getColumnIndex(PICK_IMAGE[0]);
mediaPath = c.getString(index);
c.close();
}
}
}
}
@ -95,7 +107,7 @@ public class MessagePopup extends AppCompatActivity implements OnClickListener {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults[0] == PERMISSION_GRANTED)
if (requestCode == REQ_PERM_READ && grantResults[0] == PERMISSION_GRANTED)
getMedia();
}
@ -109,15 +121,15 @@ public class MessagePopup extends AppCompatActivity implements OnClickListener {
messageAsync = new MessageUpload(this);
messageAsync.execute(username, message, mediaPath);
} else {
Toast.makeText(this, R.string.error_dm, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.error_dm, LENGTH_SHORT).show();
}
} else if (v.getId() == R.id.dm_media) {
if (mediaPath.trim().isEmpty())
getMedia();
else {
Intent image = new Intent(this, ImageDetail.class);
image.putExtra("link", new String[]{mediaPath});
image.putExtra("storable", false);
Intent image = new Intent(this, MediaViewer.class);
image.putExtra(KEY_MEDIA_LINK, new String[]{mediaPath});
image.putExtra(KEY_MEDIA_TYPE, IMAGE_STORAGE);
startActivity(image);
}
}
@ -126,16 +138,16 @@ public class MessagePopup extends AppCompatActivity implements OnClickListener {
private void getMedia() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int check = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
if (check == PackageManager.PERMISSION_GRANTED) {
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, 0);
int check = checkSelfPermission(READ_EXTERNAL_STORAGE);
if (check == PERMISSION_GRANTED) {
Intent galleryIntent = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
startActivityForResult(galleryIntent, REQ_PERM_READ);
} else {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
requestPermissions(PERM_READ, REQ_PERM_READ);
}
} else {
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, 0);
Intent galleryIntent = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
startActivityForResult(galleryIntent, REQ_PERM_READ);
}
}
}

View File

@ -1,11 +1,9 @@
package org.nuclearfog.twidda.window;
import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask.Status;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
@ -24,14 +22,24 @@ import androidx.appcompat.widget.Toolbar;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.ProfileEditor;
import org.nuclearfog.twidda.backend.ProfileEditor.Mode;
import org.nuclearfog.twidda.database.GlobalSettings;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.Intent.ACTION_PICK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.AsyncTask.Status.RUNNING;
import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.backend.ProfileEditor.Mode.READ_DATA;
import static org.nuclearfog.twidda.backend.ProfileEditor.Mode.WRITE_DATA;
public class ProfileEdit extends AppCompatActivity implements OnClickListener {
private static final String[] PERM_READ = {READ_EXTERNAL_STORAGE};
private static final String[] MEDIA_MODE = {MediaStore.Images.Media.DATA};
private static final int REQ_PERM = 3;
private static final int REQ_PB = 4;
private ProfileEditor editorAsync;
private Button txtImg;
@ -57,7 +65,7 @@ public class ProfileEdit extends AppCompatActivity implements OnClickListener {
protected void onStart() {
super.onStart();
if (editorAsync == null) {
editorAsync = new ProfileEditor(this, Mode.READ_DATA);
editorAsync = new ProfileEditor(this, READ_DATA);
editorAsync.execute();
}
}
@ -65,7 +73,7 @@ public class ProfileEdit extends AppCompatActivity implements OnClickListener {
@Override
protected void onStop() {
if (editorAsync != null && editorAsync.getStatus() == Status.RUNNING)
if (editorAsync != null && editorAsync.getStatus() == RUNNING)
editorAsync.cancel(true);
super.onStop();
}
@ -96,7 +104,7 @@ public class ProfileEdit extends AppCompatActivity implements OnClickListener {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_save) {
if (editorAsync == null || editorAsync.getStatus() != Status.RUNNING)
if (editorAsync == null || editorAsync.getStatus() != RUNNING)
save();
}
return super.onOptionsItemSelected(item);
@ -106,14 +114,15 @@ public class ProfileEdit extends AppCompatActivity implements OnClickListener {
@Override
protected void onActivityResult(int reqCode, int returnCode, Intent i) {
super.onActivityResult(reqCode, returnCode, i);
if (returnCode == RESULT_OK && i.getData() != null) {
String[] mode = {MediaStore.Images.Media.DATA};
Cursor c = getContentResolver().query(i.getData(), mode, null, null, null);
if (c != null && c.moveToFirst()) {
int index = c.getColumnIndex(mode[0]);
String mediaPath = c.getString(index);
txtImg.setText(mediaPath);
c.close();
if (i.getData() != null) {
if (reqCode == REQ_PB && returnCode == RESULT_OK) {
Cursor c = getContentResolver().query(i.getData(), MEDIA_MODE, null, null, null);
if (c != null && c.moveToFirst()) {
int index = c.getColumnIndex(MEDIA_MODE[0]);
String mediaPath = c.getString(index);
txtImg.setText(mediaPath);
c.close();
}
}
}
}
@ -121,7 +130,7 @@ public class ProfileEdit extends AppCompatActivity implements OnClickListener {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults[0] == PERMISSION_GRANTED)
if (requestCode == REQ_PERM && grantResults[0] == PERMISSION_GRANTED)
getMedia();
}
@ -137,11 +146,11 @@ public class ProfileEdit extends AppCompatActivity implements OnClickListener {
private void save() {
EditText name = findViewById(R.id.edit_name);
if (name.getText().toString().trim().isEmpty()) {
Toast.makeText(this, R.string.edit_empty_name, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.edit_empty_name, LENGTH_SHORT).show();
} else {
if (editorAsync != null && editorAsync.getStatus() == Status.RUNNING)
if (editorAsync != null && editorAsync.getStatus() == RUNNING)
editorAsync.cancel(true);
editorAsync = new ProfileEditor(this, Mode.WRITE_DATA);
editorAsync = new ProfileEditor(this, WRITE_DATA);
editorAsync.execute();
}
}
@ -149,16 +158,16 @@ public class ProfileEdit extends AppCompatActivity implements OnClickListener {
private void getMedia() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int check = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
int check = checkSelfPermission(READ_EXTERNAL_STORAGE);
if (check == PackageManager.PERMISSION_GRANTED) {
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, 0);
Intent i = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
startActivityForResult(i, REQ_PB);
} else {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
requestPermissions(PERM_READ, REQ_PERM);
}
} else {
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, 0);
Intent i = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
startActivityForResult(i, REQ_PB);
}
}
}

View File

@ -20,9 +20,12 @@ import org.nuclearfog.twidda.adapter.FragmentAdapter;
import org.nuclearfog.twidda.adapter.FragmentAdapter.AdapterType;
import org.nuclearfog.twidda.database.GlobalSettings;
import static org.nuclearfog.twidda.window.TweetPopup.KEY_TWEETPOPUP_ADDITION;
public class SearchPage extends AppCompatActivity implements OnTabSelectedListener {
public static final String KEY_SEARCH = "search";
private static final int[] icons = {R.drawable.search, R.drawable.user};
private FragmentAdapter adapter;
@ -45,8 +48,8 @@ public class SearchPage extends AppCompatActivity implements OnTabSelectedListen
getSupportActionBar().setDisplayShowTitleEnabled(false);
Bundle param = getIntent().getExtras();
if (param != null && param.containsKey("search")) {
search = param.getString("search", "");
if (param != null && param.containsKey(KEY_SEARCH)) {
search = param.getString(KEY_SEARCH);
} else if (BuildConfig.DEBUG)
throw new AssertionError();
@ -106,7 +109,7 @@ public class SearchPage extends AppCompatActivity implements OnTabSelectedListen
if (item.getItemId() == R.id.search_tweet) {
Intent intent = new Intent(this, TweetPopup.class);
if (search.startsWith("#"))
intent.putExtra("Addition", search);
intent.putExtra(KEY_TWEETPOPUP_ADDITION, search);
startActivity(intent);
}
return super.onOptionsItemSelected(item);

View File

@ -7,7 +7,6 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.Menu;
@ -38,9 +37,19 @@ import org.nuclearfog.twidda.window.UserDetail.UserType;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static android.os.AsyncTask.Status.RUNNING;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.window.SearchPage.KEY_SEARCH;
import static org.nuclearfog.twidda.window.TweetPopup.KEY_TWEETPOPUP_ADDITION;
import static org.nuclearfog.twidda.window.TweetPopup.KEY_TWEETPOPUP_REPLYID;
import static org.nuclearfog.twidda.window.UserDetail.KEY_USERLIST_ID;
import static org.nuclearfog.twidda.window.UserDetail.KEY_USERLIST_MODE;
public class TweetDetail extends AppCompatActivity implements OnClickListener, OnLongClickListener, OnTagClickListener {
public static final String KEY_TWEET_ID = "tweetID";
public static final String KEY_TWEET_NAME = "username";
public static final int STAT_CHANGED = 1;
private static final int TWEET = 2;
@ -59,9 +68,9 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
Bundle param = getIntent().getExtras();
Uri link = getIntent().getData();
if (param != null && param.containsKey("tweetID") && param.containsKey("username")) {
tweetID = param.getLong("tweetID");
username = param.getString("username");
if (param != null && param.containsKey(KEY_TWEET_ID) && param.containsKey(KEY_TWEET_NAME)) {
tweetID = param.getLong(KEY_TWEET_ID);
username = param.getString(KEY_TWEET_NAME);
} else if (link != null) {
getTweet(link.getPath());
} else if (BuildConfig.DEBUG) {
@ -110,7 +119,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
@Override
protected void onStop() {
if (statusAsync != null && statusAsync.getStatus() == Status.RUNNING)
if (statusAsync != null && statusAsync.getStatus() == RUNNING)
statusAsync.cancel(true);
super.onStop();
}
@ -118,7 +127,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
@Override
protected void onActivityResult(int reqCode, int returnCode, Intent i) {
if (reqCode == TWEET && returnCode == STAT_CHANGED) {
if (reqCode == TWEET && returnCode == STAT_CHANGED) { // TODO reinitialize list
statusAsync = null;
}
super.onActivityResult(reqCode, returnCode, i);
@ -142,7 +151,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (statusAsync != null && statusAsync.getStatus() != Status.RUNNING) {
if (statusAsync != null && statusAsync.getStatus() != RUNNING) {
switch (item.getItemId()) {
case R.id.delete_tweet:
Builder deleteDialog = new Builder(this);
@ -150,7 +159,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
deleteDialog.setPositiveButton(R.string.yes_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (statusAsync != null && statusAsync.getStatus() == Status.RUNNING)
if (statusAsync != null && statusAsync.getStatus() == RUNNING)
statusAsync.cancel(true);
statusAsync = new StatusLoader(TweetDetail.this, Mode.DELETE);
statusAsync.execute(tweetID);
@ -167,7 +176,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
intent.setData(Uri.parse(tweetLink));
startActivity(intent);
} else {
Toast.makeText(this, R.string.connection_failed, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.connection_failed, LENGTH_SHORT).show();
}
break;
@ -176,7 +185,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData linkClip = ClipData.newPlainText("tweet link", tweetLink);
clip.setPrimaryClip(linkClip);
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.copied_to_clipboard, LENGTH_SHORT).show();
break;
}
}
@ -186,26 +195,26 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
@Override
public void onClick(View v) {
if (statusAsync != null && statusAsync.getStatus() != Status.RUNNING) {
if (statusAsync != null && statusAsync.getStatus() != RUNNING) {
switch (v.getId()) {
case R.id.tweet_answer:
Intent tweet = new Intent(this, TweetPopup.class);
tweet.putExtra("TweetID", tweetID);
tweet.putExtra("Addition", username);
tweet.putExtra(KEY_TWEETPOPUP_REPLYID, tweetID);
tweet.putExtra(KEY_TWEETPOPUP_ADDITION, username);
startActivityForResult(tweet, TWEET);
break;
case R.id.tweet_retweet:
Intent userList = new Intent(this, UserDetail.class);
userList.putExtra("ID", tweetID);
userList.putExtra("mode", UserType.RETWEETS);
userList.putExtra(KEY_USERLIST_ID, tweetID);
userList.putExtra(KEY_USERLIST_MODE, UserType.RETWEETS);
startActivity(userList);
break;
case R.id.tweet_favorit:
userList = new Intent(this, UserDetail.class);
userList.putExtra("ID", tweetID);
userList.putExtra("mode", UserType.FAVORITS);
userList.putExtra(KEY_USERLIST_ID, tweetID);
userList.putExtra(KEY_USERLIST_MODE, UserType.FAVORITS);
startActivity(userList);
break;
}
@ -215,18 +224,18 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
@Override
public boolean onLongClick(View v) {
if (statusAsync != null && statusAsync.getStatus() != Status.RUNNING) {
if (statusAsync != null && statusAsync.getStatus() != RUNNING) {
switch (v.getId()) {
case R.id.tweet_retweet:
statusAsync = new StatusLoader(this, Mode.RETWEET);
statusAsync.execute(tweetID);
Toast.makeText(this, R.string.loading, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.loading, LENGTH_SHORT).show();
return true;
case R.id.tweet_favorit:
statusAsync = new StatusLoader(this, Mode.FAVORITE);
statusAsync.execute(tweetID);
Toast.makeText(this, R.string.loading, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.loading, LENGTH_SHORT).show();
return true;
}
}
@ -237,19 +246,11 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
@Override
public void onClick(String text) {
Intent intent = new Intent(this, SearchPage.class);
intent.putExtra("search", text);
intent.putExtra(KEY_SEARCH, text);
startActivity(intent);
}
public void imageClick(String[] mediaLinks) {
Intent image = new Intent(this, ImageDetail.class);
image.putExtra("link", mediaLinks);
image.putExtra("storable", true);
startActivity(image);
}
public void enableDelete() {
isHome = true;
invalidateOptionsMenu();
@ -271,7 +272,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O
link = link.substring(end + 8);
tweetID = Long.parseLong(link);
} else {
Toast.makeText(this, R.string.tweet_not_found, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.tweet_not_found, LENGTH_SHORT).show();
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();

View File

@ -3,7 +3,6 @@ package org.nuclearfog.twidda.window;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.os.AsyncTask.Status;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
@ -19,6 +18,7 @@ import androidx.appcompat.app.AppCompatActivity;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.StatusUploader;
import org.nuclearfog.twidda.backend.items.TweetHolder;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.util.LinkedList;
@ -27,19 +27,48 @@ import java.util.List;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.Intent.ACTION_PICK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.AsyncTask.Status.RUNNING;
import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_LINK;
import static org.nuclearfog.twidda.window.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.ANGIF_STORAGE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.IMAGE_STORAGE;
import static org.nuclearfog.twidda.window.MediaViewer.MediaType.VIDEO_STORAGE;
public class TweetPopup extends AppCompatActivity implements OnClickListener {
public static final String KEY_TWEETPOPUP_REPLYID = "replyID";
public static final String KEY_TWEETPOPUP_ADDITION = "Addition";
private enum Mode {
IMAGE,
VIDEO,
GIF,
NONE
}
private static final String[] READ_STORAGE = {READ_EXTERNAL_STORAGE};
private static final String[] GET_MEDIA = {MediaStore.Images.Media.DATA};
private static final String TYPE_IMAGE = "image/jpeg image/jpg image/png ";
private static final String TYPE_VIDEO = "video/mp4 video/3gp video/gif ";
private static final int PICK_MEDIA = 3;
private static final int CHECK_PERM = 4;
private StatusUploader uploaderAsync;
private View imageButton, previewBtn;
private List<String> mediaPath;
private TextView imgCount;
private EditText tweet;
private String addition = "";
private long inReplyId = -1L;
private long inReplyId = 0;
private int imgIndex = 0;
private Mode mode = Mode.NONE;
@Override
@ -49,10 +78,10 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener {
Bundle param = getIntent().getExtras();
if (param != null) {
if (param.containsKey("TweetID"))
inReplyId = param.getLong("TweetID");
if (param.containsKey("Addition"))
addition = param.getString("Addition") + " ";
if (param.containsKey(KEY_TWEETPOPUP_REPLYID))
inReplyId = param.getLong(KEY_TWEETPOPUP_REPLYID);
if (param.containsKey(KEY_TWEETPOPUP_ADDITION))
addition = param.getString(KEY_TWEETPOPUP_ADDITION) + " ";
}
mediaPath = new LinkedList<>();
@ -78,7 +107,7 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener {
@Override
protected void onDestroy() {
if (uploaderAsync != null && uploaderAsync.getStatus() == Status.RUNNING)
if (uploaderAsync != null && uploaderAsync.getStatus() == RUNNING)
uploaderAsync.cancel(true);
super.onDestroy();
}
@ -91,26 +120,47 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener {
@Override
protected void onActivityResult(int reqCode, int returnCode, Intent i) {
super.onActivityResult(reqCode, returnCode, i);
if (returnCode == RESULT_OK) {
String[] mode = {MediaStore.Images.Media.DATA};
if (i.getData() != null) {
Cursor c = getContentResolver().query(i.getData(), mode, null, null, null);
if (c != null && c.moveToFirst()) {
if (imgIndex == 0) {
previewBtn.setVisibility(View.VISIBLE);
protected void onActivityResult(int reqCode, int returnCode, Intent intent) {
super.onActivityResult(reqCode, returnCode, intent);
if (reqCode == PICK_MEDIA && returnCode == RESULT_OK) {
if (intent.getData() != null) {
Cursor cursor = getContentResolver().query(intent.getData(), GET_MEDIA, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndex(GET_MEDIA[0]);
String path = cursor.getString(index);
String ext = path.substring(path.lastIndexOf('.'));
switch (ext) {
case ".jpg":
case ".png":
mode = Mode.IMAGE;
if (imgIndex == 0) {
mediaPath.add(path);
previewBtn.setVisibility(VISIBLE);
} else if (imgIndex < 4) {
mediaPath.add(path);
String count = Integer.toString(imgIndex);
imgCount.setText(count);
if (++imgIndex == 4)
imageButton.setVisibility(INVISIBLE);
}
break;
case ".gif":
mode = Mode.GIF;
mediaPath.add(path);
previewBtn.setVisibility(VISIBLE);
imageButton.setVisibility(INVISIBLE);
break;
case ".mp4":
case ".3gp":
mode = Mode.VIDEO;
mediaPath.add(path);
previewBtn.setVisibility(VISIBLE);
imageButton.setVisibility(INVISIBLE);
break;
}
if (imgIndex < 4) {
int index = c.getColumnIndex(mode[0]);
mediaPath.add(c.getString(index));
String count = Integer.toString(++imgIndex);
imgCount.setText(count);
}
if (imgIndex == 4) {
imageButton.setVisibility(View.INVISIBLE);
}
c.close();
cursor.close();
}
}
}
@ -119,7 +169,7 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults[0] == PERMISSION_GRANTED)
if (requestCode == CHECK_PERM && grantResults[0] == PERMISSION_GRANTED)
getMedia();
}
@ -129,19 +179,21 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener {
switch (v.getId()) {
case R.id.sendTweet:
String tweetStr = tweet.getText().toString();
if (uploaderAsync != null && uploaderAsync.getStatus() == Status.RUNNING)
uploaderAsync.cancel(true);
uploaderAsync = new StatusUploader(this, tweetStr, inReplyId);
String[] paths = new String[mediaPath.size()];
paths = mediaPath.toArray(paths);
if (tweetStr.trim().isEmpty() && mediaPath.isEmpty()) {
Toast.makeText(this, R.string.empty_tweet, Toast.LENGTH_SHORT).show();
} else if (tweetStr.length() > 280) {
Toast.makeText(this, R.string.char_limit_reached, Toast.LENGTH_SHORT).show();
} else if (!mediaPath.isEmpty()) {
String[] paths = new String[mediaPath.size()];
paths = mediaPath.toArray(paths);
uploaderAsync.execute(paths);
} else {
if (uploaderAsync != null && uploaderAsync.getStatus() == RUNNING)
uploaderAsync.cancel(true);
if (tweetStr.trim().isEmpty() && paths.length == 0) {
Toast.makeText(this, R.string.empty_tweet, LENGTH_SHORT).show();
} else if (paths.length > 0) {
TweetHolder tweet = new TweetHolder(tweetStr, inReplyId, paths);
uploaderAsync = new StatusUploader(this, tweet);
uploaderAsync.execute();
} else if (!tweetStr.trim().isEmpty()) {
TweetHolder tweet = new TweetHolder(tweetStr, inReplyId);
uploaderAsync = new StatusUploader(this, tweet);
uploaderAsync.execute();
}
break;
@ -151,21 +203,36 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener {
break;
case R.id.image:
getMedia();
checkPermission();
break;
case R.id.img_preview:
Intent image = new Intent(this, ImageDetail.class);
image.putExtra("link", mediaPath.toArray(new String[0]));
image.putExtra("storable", false);
startActivity(image);
Intent image = new Intent(this, MediaViewer.class);
image.putExtra(KEY_MEDIA_LINK, mediaPath.toArray(new String[0]));
switch (mode) {
case IMAGE:
image.putExtra(KEY_MEDIA_TYPE, IMAGE_STORAGE);
startActivity(image);
break;
case VIDEO:
image.putExtra(KEY_MEDIA_TYPE, VIDEO_STORAGE);
startActivity(image);
break;
case GIF:
image.putExtra(KEY_MEDIA_TYPE, ANGIF_STORAGE);
startActivity(image);
break;
}
break;
}
}
public void close() {
Toast.makeText(this, R.string.tweet_sent, Toast.LENGTH_LONG).show();
Toast.makeText(this, R.string.tweet_sent, LENGTH_LONG).show();
finish();
}
@ -188,18 +255,27 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener {
}
private void getMedia() {
private void checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int check = checkSelfPermission(READ_EXTERNAL_STORAGE);
if (check == PERMISSION_GRANTED) {
Intent mediaIntent = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
startActivityForResult(mediaIntent, 0);
getMedia();
} else {
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, 1);
requestPermissions(READ_STORAGE, CHECK_PERM);
}
} else {
Intent mediaIntent = new Intent(Intent.ACTION_PICK, EXTERNAL_CONTENT_URI);
startActivityForResult(mediaIntent, 0);
getMedia();
}
}
private void getMedia() {
if (mode == Mode.NONE) {
Intent mediaIntent = new Intent(ACTION_PICK);
mediaIntent.setDataAndType(EXTERNAL_CONTENT_URI, TYPE_IMAGE + TYPE_VIDEO);
startActivityForResult(mediaIntent, PICK_MEDIA);
} else if (mode == Mode.IMAGE) {
Intent imageIntent = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
startActivityForResult(imageIntent, PICK_MEDIA);
}
}
}

View File

@ -15,6 +15,8 @@ import org.nuclearfog.twidda.database.GlobalSettings;
public class UserDetail extends AppCompatActivity {
public static final String KEY_USERLIST_MODE = "mode";
public static final String KEY_USERLIST_ID = "id";
public enum UserType {
FRIENDS,
FOLLOWERS,
@ -31,9 +33,9 @@ public class UserDetail extends AppCompatActivity {
setContentView(R.layout.page_userlist);
Bundle param = getIntent().getExtras();
if (param != null && param.containsKey("mode") && param.containsKey("ID")) {
mode = (UserType) param.getSerializable("mode");
id = param.getLong("ID");
if (param != null && param.containsKey(KEY_USERLIST_MODE) && param.containsKey(KEY_USERLIST_ID)) {
mode = (UserType) param.getSerializable(KEY_USERLIST_MODE);
id = param.getLong(KEY_USERLIST_ID);
} else if (BuildConfig.DEBUG) {
throw new AssertionError();
}

View File

@ -1,6 +1,5 @@
package org.nuclearfog.twidda.window;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
@ -36,9 +35,22 @@ import org.nuclearfog.twidda.database.GlobalSettings;
import java.text.NumberFormat;
import static android.content.Intent.ACTION_VIEW;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.window.MessagePopup.KEY_DM_ADDITION;
import static org.nuclearfog.twidda.window.SearchPage.KEY_SEARCH;
import static org.nuclearfog.twidda.window.TweetPopup.KEY_TWEETPOPUP_ADDITION;
import static org.nuclearfog.twidda.window.UserDetail.KEY_USERLIST_ID;
import static org.nuclearfog.twidda.window.UserDetail.KEY_USERLIST_MODE;
import static org.nuclearfog.twidda.window.UserDetail.UserType.FOLLOWERS;
import static org.nuclearfog.twidda.window.UserDetail.UserType.FRIENDS;
public class UserProfile extends AppCompatActivity implements OnClickListener, OnTagClickListener, OnTabSelectedListener {
public static final String KEY_PROFILE_ID = "userID";
public static final String KEY_PROFILE_NAME = "username";
private ProfileLoader profileAsync;
private FragmentAdapter adapter;
private ViewPager pager;
@ -58,9 +70,9 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
setContentView(R.layout.page_profile);
Bundle param = getIntent().getExtras();
if (param != null && param.containsKey("userID") && param.containsKey("username")) {
userId = param.getLong("userID");
username = param.getString("username");
if (param != null && param.containsKey(KEY_PROFILE_ID) && param.containsKey(KEY_PROFILE_NAME)) {
userId = param.getLong(KEY_PROFILE_ID);
username = param.getString(KEY_PROFILE_NAME);
} else if (BuildConfig.DEBUG) {
throw new AssertionError();
}
@ -184,7 +196,7 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
case R.id.profile_tweet:
Intent tweet = new Intent(this, TweetPopup.class);
if (!home)
tweet.putExtra("Addition", username);
tweet.putExtra(KEY_TWEETPOPUP_ADDITION, username);
startActivity(tweet);
break;
@ -232,9 +244,9 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
Intent dm = new Intent(this, DirectMessage.class);
startActivity(dm);
} else {
Intent sendDm = new Intent(this, MessagePopup.class);
sendDm.putExtra("username", username);
startActivity(sendDm);
Intent dmPopup = new Intent(this, MessagePopup.class);
dmPopup.putExtra(KEY_DM_ADDITION, username);
startActivity(dmPopup);
}
break;
@ -262,7 +274,7 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
@Override
public void onClick(String text) {
Intent intent = new Intent(this, SearchPage.class);
intent.putExtra("search", text);
intent.putExtra(KEY_SEARCH, text);
startActivity(intent);
}
@ -273,8 +285,8 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
case R.id.following:
if (!isLocked) {
Intent following = new Intent(this, UserDetail.class);
following.putExtra("ID", userId);
following.putExtra("mode", UserDetail.UserType.FRIENDS);
following.putExtra(KEY_USERLIST_ID, userId);
following.putExtra(KEY_USERLIST_MODE, FRIENDS);
startActivity(following);
}
break;
@ -282,21 +294,21 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
case R.id.follower:
if (!isLocked) {
Intent follower = new Intent(this, UserDetail.class);
follower.putExtra("ID", userId);
follower.putExtra("mode", UserDetail.UserType.FOLLOWERS);
follower.putExtra(KEY_USERLIST_ID, userId);
follower.putExtra(KEY_USERLIST_MODE, FOLLOWERS);
startActivity(follower);
}
break;
case R.id.links:
ConnectivityManager mConnect = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
ConnectivityManager mConnect = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
if (mConnect.getActiveNetworkInfo() != null && mConnect.getActiveNetworkInfo().isConnected()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
Intent browserIntent = new Intent(ACTION_VIEW);
String link = lnkTxt.getText().toString();
intent.setData(Uri.parse(link));
startActivity(intent);
browserIntent.setData(Uri.parse(link));
startActivity(browserIntent);
} else {
Toast.makeText(this, R.string.connection_failed, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.connection_failed, LENGTH_SHORT).show();
}
break;
}

View File

@ -5,5 +5,5 @@
android:viewportHeight="20.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M10,8c-1.657,0 -3,1.344 -3,3s1.343,3 3,3c1.656,0 3,-1.344 3,-3S11.656,8 10,8zM18,5h-2.4c-0.33,0 -0.686,-0.256 -0.789,-0.57L14.19,2.569C14.085,2.256 13.731,2 13.4,2H6.6C6.27,2 5.914,2.256 5.811,2.568L5.189,4.43C5.085,4.744 4.73,5 4.4,5H2C0.9,5 0,5.9 0,7v9c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C20,5.9 19.1,5 18,5zM10,16c-2.762,0 -5,-2.238 -5,-5s2.238,-5 5,-5c2.761,0 5,2.238 5,5S12.761,16 10,16zM17.5,8.2c-0.387,0 -0.7,-0.314 -0.7,-0.701c0,-0.385 0.313,-0.7 0.7,-0.7s0.7,0.314 0.7,0.7C18.2,7.886 17.887,8.2 17.5,8.2z" />
android:pathData="M19,2H1C0.447,2 0,2.447 0,3v14c0,0.552 0.447,1 1,1h18c0.553,0 1,-0.448 1,-1V3C20,2.448 19.553,2 19,2zM18,16H2V4h16V16zM14.315,10.877l-3.231,1.605l-3.77,-6.101L4,14h12L14.315,10.877zM13.25,9c0.69,0 1.25,-0.56 1.25,-1.25S13.94,6.5 13.25,6.5S12,7.06 12,7.75S12.56,9 13.25,9z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="20.0"
android:viewportHeight="20.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M10,8c-1.657,0 -3,1.344 -3,3s1.343,3 3,3c1.656,0 3,-1.344 3,-3S11.656,8 10,8zM18,5h-2.4c-0.33,0 -0.686,-0.256 -0.789,-0.57L14.19,2.569C14.085,2.256 13.731,2 13.4,2H6.6C6.27,2 5.914,2.256 5.811,2.568L5.189,4.43C5.085,4.744 4.73,5 4.4,5H2C0.9,5 0,5.9 0,7v9c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C20,5.9 19.1,5 18,5zM10,16c-2.762,0 -5,-2.238 -5,-5s2.238,-5 5,-5c2.761,0 5,2.238 5,5S12.761,16 10,16zM17.5,8.2c-0.387,0 -0.7,-0.314 -0.7,-0.701c0,-0.385 0.313,-0.7 0.7,-0.7s0.7,0.314 0.7,0.7C18.2,7.886 17.887,8.2 17.5,8.2z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="20.0"
android:viewportHeight="20.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M19,2H1C0.447,2 0,2.447 0,3v14c0,0.552 0.447,1 1,1h18c0.553,0 1,-0.448 1,-1V3C20,2.448 19.553,2 19,2zM18,16H2V4h16V16zM14.315,10.877l-3.231,1.605l-3.77,-6.101L4,14h12L14.315,10.877zM13.25,9c0.69,0 1.25,-0.56 1.25,-1.25S13.94,6.5 13.25,6.5S12,7.06 12,7.75S12.56,9 13.25,9z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M20,5V3.799C20,3.357 19.643,3 19.201,3h-18.4C0.358,3 0,3.357 0,3.799V5h2v2H0v2h2v2H0v2h2v2H0v1.199C0,16.641 0.358,17 0.801,17h18.4C19.643,17 20,16.641 20,16.199V15h-2v-2h2v-2h-2V9h2V7h-2V5H20zM8,13V7l5,3L8,13z"
android:fillColor="#FFFFFF" />
</vector>

View File

@ -1,19 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
android:layout_margin="@dimen/preview_margin">
<FrameLayout
android:id="@+id/image_window"
android:layout_width="match_parent"
android:layout_height="@dimen/image_height">
android:layout_height="match_parent"
android:visibility="gone">
<org.nuclearfog.zoomview.ZoomView
android:id="@+id/image_full"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
app:enable_move="true"
app:max_zoom_in="10.0"
app:max_zoom_out="0.8" />
<ProgressBar
android:id="@+id/image_load"
@ -21,12 +25,32 @@
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/image_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:scrollbars="horizontal" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/image_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_layout" />
<FrameLayout
android:id="@+id/video_window"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
</LinearLayout>
<VideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<MediaController
android:id="@+id/video_controller"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
</FrameLayout>
</FrameLayout>

View File

@ -122,7 +122,17 @@
android:contentDescription="@string/image_preview_button"
android:background="@drawable/button"
android:visibility="gone"
app:srcCompat="@drawable/preview" />
app:srcCompat="@drawable/image" />
<ImageButton
android:id="@+id/video_attach"
android:layout_width="@dimen/button_media_width"
android:layout_height="@dimen/button_media_height"
android:layout_margin="@dimen/margin_tweet_icon"
android:contentDescription="@string/image_preview_button"
android:background="@drawable/button"
android:visibility="gone"
app:srcCompat="@drawable/video" />
</LinearLayout>

View File

@ -39,7 +39,7 @@
android:layout_marginLeft="@dimen/margin_dm_icon"
android:background="@drawable/button"
android:contentDescription="@string/add_image"
app:srcCompat="@drawable/image" />
app:srcCompat="@drawable/image_add" />
<ImageButton
android:id="@+id/dm_send"

View File

@ -46,7 +46,7 @@
android:background="@drawable/button"
android:contentDescription="@string/image_preview"
android:visibility="invisible"
app:srcCompat="@drawable/preview" />
app:srcCompat="@drawable/image" />
<ImageButton
android:id="@+id/image"
@ -56,7 +56,7 @@
android:layout_marginLeft="@dimen/margin_tweet_icon"
android:background="@drawable/button"
android:contentDescription="@string/add_image"
app:srcCompat="@drawable/image" />
app:srcCompat="@drawable/image_add" />
<ImageButton
android:id="@+id/sendTweet"

View File

@ -98,4 +98,5 @@
<string name="settings_info">Information</string>
<string name="follows_you">folgt dir</string>
<string name="image_store_failure">Fehler beim Speichern!</string>
<string name="not_authorized">Keine Berechtigung!</string>
</resources>

View File

@ -6,7 +6,7 @@
<dimen name="profile_image">64dp</dimen>
<dimen name="profile_middle">40dp</dimen>
<dimen name="profile_small">36dp</dimen>
<dimen name="image_height">300dp</dimen>
<dimen name="image_height">400dp</dimen>
<dimen name="tweet_profile">56dp</dimen>
<dimen name="image_tab_ico">18dp</dimen>
@ -51,4 +51,6 @@
<dimen name="button_tweet_size">40dp</dimen>
<dimen name="button_media_height">36dp</dimen>
<dimen name="button_media_width">64dp</dimen>
<dimen name="preview_divider_margin">5dp</dimen>
<dimen name="preview_margin">10dp</dimen>
</resources>

View File

@ -107,4 +107,5 @@
<string name="github_link" translatable="false">github.com/nuclearfog/Shitter</string>
<string name="follows_you">follows you</string>
<string name="image_store_failure">image save failed!</string>
<string name="not_authorized">Not authorized!</string>
</resources>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -9,7 +9,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
org.gradle.jvmargs=-Xmx768m
org.gradle.configureondemand=false;
android.useAndroidX=true
android.enableJetifier=true

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4-all.zip