diff --git a/app/build.gradle b/app/build.gradle index 76eeffe..5f65147 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,6 +62,10 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' implementation 'androidx.preference:preference:1.1.1' + implementation 'androidx.camera:camera-camera2:1.0.0-beta03' + implementation 'androidx.camera:camera-lifecycle:1.0.0-beta03' + implementation 'androidx.camera:camera-view:1.0.0-alpha10' + implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.squareup.okhttp3:okhttp:4.4.1' implementation 'com.google.firebase:firebase-analytics:17.4.0' @@ -91,8 +95,8 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - __32bitImplementation files('libs/lbrysdk-0.73.1-release__arm.aar') - __64bitImplementation files('libs/lbrysdk-0.73.1-release__arm64.aar') + __32bitImplementation files('libs/lbrysdk-0.74.0-release__arm.aar') + __64bitImplementation files('libs/lbrysdk-0.74.0-release__arm64.aar') } apply plugin: 'com.google.gms.google-services' diff --git a/app/libs/lbrysdk-0.73.1-release__arm.aar b/app/libs/lbrysdk-0.74.0-release__arm.aar similarity index 70% rename from app/libs/lbrysdk-0.73.1-release__arm.aar rename to app/libs/lbrysdk-0.74.0-release__arm.aar index 025cce9..f917e37 100644 Binary files a/app/libs/lbrysdk-0.73.1-release__arm.aar and b/app/libs/lbrysdk-0.74.0-release__arm.aar differ diff --git a/app/libs/lbrysdk-0.73.1-release__arm64.aar b/app/libs/lbrysdk-0.74.0-release__arm64.aar similarity index 71% rename from app/libs/lbrysdk-0.73.1-release__arm64.aar rename to app/libs/lbrysdk-0.74.0-release__arm64.aar index 7d5e4b9..fe87414 100644 Binary files a/app/libs/lbrysdk-0.73.1-release__arm64.aar and b/app/libs/lbrysdk-0.74.0-release__arm64.aar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aa76201..f682d17 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,11 +3,12 @@ package="io.lbry.browser" android:installLocation="auto"> + + - cameraPermissionListeners; private List downloadActionListeners; private List sdkStatusListeners; private List storagePermissionListeners; @@ -296,25 +301,6 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener private static final int STARTUP_STAGE_SUBSCRIPTIONS_LOADED = 6; private static final int STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED = 7; - private final List supportedMenuItemIds = Arrays.asList( - // find content - NavMenuItem.ID_ITEM_FOLLOWING, - NavMenuItem.ID_ITEM_EDITORS_CHOICE, - NavMenuItem.ID_ITEM_ALL_CONTENT, - - // your content - NavMenuItem.ID_ITEM_CHANNELS, - NavMenuItem.ID_ITEM_LIBRARY, - - // wallet - NavMenuItem.ID_ITEM_WALLET, - NavMenuItem.ID_ITEM_REWARDS, - NavMenuItem.ID_ITEM_INVITES, - - NavMenuItem.ID_ITEM_SETTINGS, - NavMenuItem.ID_ITEM_ABOUT - ); - public boolean isDarkMode() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); return sp.getBoolean(PREFERENCE_KEY_DARK_MODE, false); @@ -391,6 +377,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener // other pendingSyncSetQueue = new ArrayList<>(); openNavFragments = new HashMap<>(); + cameraPermissionListeners = new ArrayList<>(); downloadActionListeners = new ArrayList<>(); sdkStatusListeners = new ArrayList<>(); storagePermissionListeners = new ArrayList<>(); @@ -468,13 +455,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener return; } - if (!supportedMenuItemIds.contains(menuItem.getId())) { - Snackbar.make(navItemsView, R.string.not_yet_implemented, Snackbar.LENGTH_LONG).show(); - } else { - navMenuAdapter.setCurrentItem(menuItem); - shouldOpenUserSelectedMenuItem = true; - selectedMenuItemId = menuItem.getId(); - } + navMenuAdapter.setCurrentItem(menuItem); + shouldOpenUserSelectedMenuItem = true; + selectedMenuItemId = menuItem.getId(); closeDrawer(); } }); @@ -496,8 +479,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener specialRouteFragmentClassMap.put("invite", InvitesFragment.class); specialRouteFragmentClassMap.put("invites", InvitesFragment.class); specialRouteFragmentClassMap.put("library", LibraryFragment.class); - //specialRouteFragmentClassMap.put("publish", PublishFragment.class); - //specialRouteFragmentClassMap.put("publishes", PublishesFragment.class); + specialRouteFragmentClassMap.put("publish", PublishFragment.class); + specialRouteFragmentClassMap.put("publishes", PublishesFragment.class); specialRouteFragmentClassMap.put("following", FollowingFragment.class); specialRouteFragmentClassMap.put("rewards", RewardsFragment.class); specialRouteFragmentClassMap.put("settings", SettingsFragment.class); @@ -512,6 +495,16 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener checkNotificationOpenIntent(intent); } + public void addCameraPermissionListener(CameraPermissionListener listener) { + if (!cameraPermissionListeners.contains(listener)) { + cameraPermissionListeners.add(listener); + } + } + + public void removeCameraPermissionListener(CameraPermissionListener listener) { + cameraPermissionListeners.remove(listener); + } + public void addDownloadActionListener(DownloadActionListener listener) { if (!downloadActionListeners.contains(listener)) { downloadActionListeners.add(listener); @@ -581,12 +574,18 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener openFragment(AllContentFragment.class, true, NavMenuItem.ID_ITEM_ALL_CONTENT); break; + case NavMenuItem.ID_ITEM_NEW_PUBLISH: + openFragment(PublishFragment.class, true, NavMenuItem.ID_ITEM_NEW_PUBLISH); + break; case NavMenuItem.ID_ITEM_CHANNELS: openFragment(ChannelManagerFragment.class, true, NavMenuItem.ID_ITEM_CHANNELS); break; case NavMenuItem.ID_ITEM_LIBRARY: openFragment(LibraryFragment.class, true, NavMenuItem.ID_ITEM_LIBRARY); break; + case NavMenuItem.ID_ITEM_PUBLISHES: + openFragment(PublishesFragment.class, true, NavMenuItem.ID_ITEM_PUBLISHES); + break; case NavMenuItem.ID_ITEM_WALLET: openFragment(WalletFragment.class, true, NavMenuItem.ID_ITEM_WALLET); @@ -818,6 +817,20 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener super.onPause(); } + public static void suspendGlobalPlayer(Context context) { + if (MainActivity.appPlayer != null) { + MainActivity.appPlayer.setPlayWhenReady(false); + } + if (context instanceof MainActivity) { + ((MainActivity) context).hideGlobalNowPlaying(); + } + } + public static void resumeGlobalPlayer(Context context) { + if (context instanceof MainActivity) { + ((MainActivity) context).checkNowPlaying(); + } + } + private void toggleUrlSuggestions(boolean visible) { View container = findViewById(R.id.url_suggestions_container); View closeIcon = findViewById(R.id.wunderbar_close); @@ -830,7 +843,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public int getScaledValue(int value) { float scale = getResources().getDisplayMetrics().density; - return (int) (value * scale + 0.5f); + return Helper.getScaledValue(value, scale); } private void setupUriBar() { @@ -1249,6 +1262,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener startup(); } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) { openFragment(FollowingFragment.class, false, NavMenuItem.ID_ITEM_FOLLOWING); + fetchRewards(); } } @@ -1340,7 +1354,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener if (navMenuAdapter != null) { navMenuAdapter.setExtraLabelForItem( NavMenuItem.ID_ITEM_WALLET, - Lbryio.LBCUSDRate > 0 ? String.format("$%s", Helper.USD_CURRENCY_FORMAT.format(usdBalance)) : null + Lbryio.LBCUSDRate > 0 ? String.format("$%s", Helper.SIMPLE_CURRENCY_FORMAT.format(usdBalance)) : null ); } } @@ -1750,9 +1764,21 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener listener.onStoragePermissionRefused(); } } - startingStoragePermissionRequest = false; + startingPermissionRequest = false; break; + case REQUEST_CAMERA_PERMISSION: + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + for (CameraPermissionListener listener : cameraPermissionListeners) { + listener.onCameraPermissionGranted(); + } + } else { + for (CameraPermissionListener listener : cameraPermissionListeners) { + listener.onCameraPermissionRefused(); + } + } + startingPermissionRequest = false; + break; } } @@ -2015,7 +2041,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener if (navMenuAdapter != null) { navMenuAdapter.setExtraLabelForItem( NavMenuItem.ID_ITEM_REWARDS, - Lbryio.LBCUSDRate > 0 ? String.format("$%s", Helper.USD_CURRENCY_FORMAT.format(usdRewardAmount)) : null + Lbryio.LBCUSDRate > 0 ? String.format("$%s", Helper.SIMPLE_CURRENCY_FORMAT.format(usdRewardAmount)) : null ); } } @@ -2115,7 +2141,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener }, 1000); return; } - if (startingStoragePermissionRequest) { + if (startingPermissionRequest) { return; } enterPIPMode(); @@ -2241,10 +2267,10 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener )); yourContentGroup.setItems(Arrays.asList( - //new NavMenuItem(NavMenuItem.ID_ITEM_NEW_PUBLISH, R.string.fa_upload, R.string.new_publish, "NewPublish", context), + new NavMenuItem(NavMenuItem.ID_ITEM_NEW_PUBLISH, R.string.fa_upload, R.string.new_publish, "NewPublish", context), new NavMenuItem(NavMenuItem.ID_ITEM_CHANNELS, R.string.fa_at, R.string.channels, "Channels", context), - new NavMenuItem(NavMenuItem.ID_ITEM_LIBRARY, R.string.fa_download, R.string.library, "Library", context) - //new NavMenuItem(NavMenuItem.ID_ITEM_PUBLISHES, R.string.fa_cloud_upload, R.string.publishes, "Publishes", context) + new NavMenuItem(NavMenuItem.ID_ITEM_LIBRARY, R.string.fa_download, R.string.library, "Library", context), + new NavMenuItem(NavMenuItem.ID_ITEM_PUBLISHES, R.string.fa_cloud_upload, R.string.publishes, "Publishes", context) )); walletGroup.setItems(Arrays.asList( @@ -2518,7 +2544,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener if (!forceRequest && ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, permission)) { Toast.makeText(context, rationale, Toast.LENGTH_LONG).show(); } else { - startingStoragePermissionRequest = true; + startingPermissionRequest = true; ActivityCompat.requestPermissions((Activity) context, new String[] { permission }, requestCode); } } diff --git a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java index 23f9532..75a64bd 100644 --- a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java +++ b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java @@ -96,6 +96,14 @@ public class ClaimListAdapter extends RecyclerView.Adapter(this.items); } + public void updateSigningChannelForClaim(Claim resolvedClaim) { + for (Claim claim : items) { + if (claim.getClaimId().equalsIgnoreCase(resolvedClaim.getClaimId())) { + claim.setSigningChannel(resolvedClaim.getSigningChannel()); + } + } + } + public void clearItems() { clearSelectedItems(); this.items.clear(); @@ -413,7 +421,10 @@ public class ClaimListAdapter extends RecyclerView.Adapter 0 ? Double.valueOf(((double) claimFile.getWrittenBytes() / (double) claimFile.getTotalBytes()) * 100.0).intValue() : 0; diff --git a/app/src/main/java/io/lbry/browser/adapter/GalleryGridAdapter.java b/app/src/main/java/io/lbry/browser/adapter/GalleryGridAdapter.java new file mode 100644 index 0000000..285f767 --- /dev/null +++ b/app/src/main/java/io/lbry/browser/adapter/GalleryGridAdapter.java @@ -0,0 +1,122 @@ +package io.lbry.browser.adapter; + +import android.content.Context; +import android.graphics.Rect; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; + +import java.util.ArrayList; +import java.util.List; + +import io.lbry.browser.R; +import io.lbry.browser.listener.ChannelItemSelectionListener; +import io.lbry.browser.model.Claim; +import io.lbry.browser.model.GalleryItem; +import io.lbry.browser.utils.Helper; +import lombok.Setter; + +public class GalleryGridAdapter extends RecyclerView.Adapter { + private Context context; + private List items; + @Setter + private GalleryItemClickListener listener; + + public GalleryGridAdapter(List items, Context context) { + this.items = new ArrayList<>(items); + this.context = context; + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + protected ImageView thumbnailView; + protected TextView durationView; + public ViewHolder(View v) { + super(v); + thumbnailView = v.findViewById(R.id.gallery_item_thumbnail); + durationView = v.findViewById(R.id.gallery_item_duration); + } + } + + public int getItemCount() { + return items != null ? items.size() : 0; + } + + public void addItem(GalleryItem item) { + if (!items.contains(item)) { + items.add(item); + notifyDataSetChanged(); + } + } + + public void addItems(List items) { + for (GalleryItem item : items) { + if (!this.items.contains(item)) { + this.items.add(item); + notifyDataSetChanged(); + } + } + } + + public void clearItems() { + items.clear(); + notifyDataSetChanged(); + } + + @Override + public GalleryGridAdapter.ViewHolder onCreateViewHolder(ViewGroup root, int viewType) { + View v = LayoutInflater.from(context).inflate(R.layout.list_item_gallery, root, false); + return new GalleryGridAdapter.ViewHolder(v); + } + + @Override + public void onBindViewHolder(GalleryGridAdapter.ViewHolder vh, int position) { + GalleryItem item = items.get(position); + String thumbnailUrl = item.getThumbnailUrl(); + Glide.with(context.getApplicationContext()).load(thumbnailUrl).centerCrop().into(vh.thumbnailView); + vh.durationView.setVisibility(item.getDuration() > 0 ? View.VISIBLE : View.INVISIBLE); + vh.durationView.setText(item.getDuration() > 0 ? Helper.formatDuration(Double.valueOf(item.getDuration() / 1000.0).longValue()) : null); + + vh.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (listener != null) { + listener.onGalleryItemClicked(item); + } + } + }); + } + + public interface GalleryItemClickListener { + void onGalleryItemClicked(GalleryItem item); + } + + public static class GalleryGridItemDecoration extends RecyclerView.ItemDecoration { + + private int spanCount; + private int spacing; + + public GalleryGridItemDecoration(int spanCount, int spacing) { + this.spanCount = spanCount; + this.spacing = spacing; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + int position = parent.getChildAdapterPosition(view); // item position + int column = position % spanCount; // item column + + outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) + outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) + if (position >= spanCount) { + outRect.top = spacing; // item top + } + } + } +} diff --git a/app/src/main/java/io/lbry/browser/adapter/RewardListAdapter.java b/app/src/main/java/io/lbry/browser/adapter/RewardListAdapter.java index e5ea6c4..71d1bee 100644 --- a/app/src/main/java/io/lbry/browser/adapter/RewardListAdapter.java +++ b/app/src/main/java/io/lbry/browser/adapter/RewardListAdapter.java @@ -159,7 +159,7 @@ public class RewardListAdapter extends RecyclerView.Adapter> { - private String type; + private List types; private View progressView; private ClaimListResultHandler handler; private Exception error; public ClaimListTask(String type, View progressView, ClaimListResultHandler handler) { - this.type = type; + this(Arrays.asList(type), progressView, handler); + } + public ClaimListTask(List types, View progressView, ClaimListResultHandler handler) { + this.types = types; this.progressView = progressView; this.handler = handler; } @@ -36,8 +40,8 @@ public class ClaimListTask extends AsyncTask> { try { Map options = new HashMap<>(); - if (!Helper.isNullOrEmpty(type)) { - options.put("claim_type", type); + if (types != null && types.size() > 0) { + options.put("claim_type", types); } options.put("page", 1); options.put("page_size", 999); diff --git a/app/src/main/java/io/lbry/browser/tasks/localdata/LoadGalleryItemsTask.java b/app/src/main/java/io/lbry/browser/tasks/localdata/LoadGalleryItemsTask.java new file mode 100644 index 0000000..154a5dc --- /dev/null +++ b/app/src/main/java/io/lbry/browser/tasks/localdata/LoadGalleryItemsTask.java @@ -0,0 +1,137 @@ +package io.lbry.browser.tasks.localdata; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.MediaStore; +import android.util.Log; +import android.view.View; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.lbry.browser.model.GalleryItem; +import io.lbry.browser.utils.Helper; + +public class LoadGalleryItemsTask extends AsyncTask> { + private static final String TAG = "LoadGalleryItemsTask"; + private LoadGalleryHandler handler; + private View progressView; + private Context context; + + public LoadGalleryItemsTask(View progressView, Context context, LoadGalleryHandler handler) { + this.progressView = progressView; + this.context = context; + this.handler = handler; + } + + protected void onPreExecute(Void... params) { + Helper.setViewVisibility(progressView, View.VISIBLE); + } + + protected List doInBackground(Void... params) { + List items = new ArrayList<>(); + List itemsWithThumbnails = new ArrayList<>(); + Cursor cursor = null; + if (context != null) { + ContentResolver resolver = context.getContentResolver(); + try { + String[] projection = { + MediaStore.MediaColumns._ID, + MediaStore.MediaColumns.DATA, + MediaStore.MediaColumns.DISPLAY_NAME, + MediaStore.MediaColumns.MIME_TYPE, + MediaStore.Video.Media.DURATION + }; + cursor = resolver.query( + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, + projection, null, null, + String.format("%s DESC", MediaStore.MediaColumns.DATE_MODIFIED)); + while (cursor.moveToNext()) { + int idColumn = cursor.getColumnIndex(MediaStore.MediaColumns._ID); + int nameColumn = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME); + int typeColumn = cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE); + int pathColumn = cursor.getColumnIndex(MediaStore.MediaColumns.DATA); + int durationColumn = cursor.getColumnIndex(MediaStore.Video.Media.DURATION); + + String id = cursor.getString(idColumn); + GalleryItem item = new GalleryItem(); + item.setId(id); + item.setName(cursor.getString(nameColumn)); + item.setType(cursor.getString(typeColumn)); + item.setFilePath(cursor.getString(pathColumn)); + item.setDuration(cursor.getLong(durationColumn)); + items.add(item); + } + } catch (SQLiteException ex) { + + // failed to load videos. log and pass + Log.e(TAG, ex.getMessage(), ex); + } finally { + Helper.closeCursor(cursor); + } + + // load (or generate) thumbnail for each item + for (GalleryItem item : items) { + String id = item.getId(); + File cacheDir = context.getExternalCacheDir(); + File thumbnailsDir = new File(String.format("%s/thumbnails", cacheDir.getAbsolutePath())); + if (!thumbnailsDir.isDirectory()) { + thumbnailsDir.mkdirs(); + } + + String thumbnailPath = String.format("%s/%s.png", thumbnailsDir.getAbsolutePath(), id); + File file = new File(thumbnailPath); + if (!file.exists()) { + // save the thumbnail to the path + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = 1; + Bitmap thumbnail = MediaStore.Video.Thumbnails.getThumbnail( + resolver, Long.parseLong(id), MediaStore.Video.Thumbnails.MINI_KIND, options); + if (thumbnail != null) { + try (FileOutputStream os = new FileOutputStream(thumbnailPath)) { + thumbnail.compress(Bitmap.CompressFormat.PNG, 80, os); + } catch (IOException ex) { + // skip + } + } + } + + if (file.exists() && file.length() > 0) { + item.setThumbnailUrl(Uri.fromFile(file).toString()); + itemsWithThumbnails.add(item); + publishProgress(item); + } + } + } + + return itemsWithThumbnails; + } + + protected void onProgressUpdate(GalleryItem... items) { + if (handler != null) { + for (GalleryItem item : items) { + handler.onItemLoaded(item); + } + } + } + + protected void onPostExecute(List items) { + if (handler != null) { + handler.onAllItemsLoaded(items); + } + } + + public interface LoadGalleryHandler { + void onItemLoaded(GalleryItem item); + void onAllItemsLoaded(List items); + } +} diff --git a/app/src/main/java/io/lbry/browser/ui/BaseFragment.java b/app/src/main/java/io/lbry/browser/ui/BaseFragment.java index bc3afeb..9e16adc 100644 --- a/app/src/main/java/io/lbry/browser/ui/BaseFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/BaseFragment.java @@ -16,6 +16,34 @@ public class BaseFragment extends Fragment { @Setter private Map params; + public boolean shouldHideGlobalPlayer() { + return false; + } + + public boolean shouldSuspendGlobalPlayer() { + return false; + } + + public void onStart() { + super.onStart(); + if (shouldSuspendGlobalPlayer()) { + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity.suspendGlobalPlayer(context); + } + } + } + + public void onStop() { + if (shouldSuspendGlobalPlayer()) { + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity.resumeGlobalPlayer(context); + } + } + super.onStop(); + } + public void onResume() { super.onResume(); Context context = getContext(); @@ -23,12 +51,11 @@ public class BaseFragment extends Fragment { MainActivity activity = (MainActivity) context; activity.setSelectedMenuItemForFragment(this); - if (this instanceof FileViewFragment) { + if (shouldHideGlobalPlayer()) { activity.hideGlobalNowPlaying(); } else { activity.checkNowPlaying(); } } } - } diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java index e00d673..6c88ccf 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java @@ -214,7 +214,7 @@ public class ChannelContentFragment extends Fragment implements DownloadActionLi Context context = getContext(); boolean canShowMatureContent = false; if (context != null) { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false); } diff --git a/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java b/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java index e8c84eb..7284631 100644 --- a/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java @@ -935,111 +935,114 @@ public class FileViewFragment extends BaseFragment implements loadViewCount(); checkIsFollowing(); + + View root = getView(); + if (root != null) { + root.findViewById(R.id.file_view_scroll_view).scrollTo(0, 0); + Helper.setViewVisibility(layoutDisplayArea, View.VISIBLE); - getView().findViewById(R.id.file_view_scroll_view).scrollTo(0, 0); - Helper.setViewVisibility(layoutDisplayArea, View.VISIBLE); + ImageView descIndicator = root.findViewById(R.id.file_view_desc_toggle_arrow); + descIndicator.setImageResource(R.drawable.ic_arrow_dropdown); - ImageView descIndicator = getView().findViewById(R.id.file_view_desc_toggle_arrow); - descIndicator.setImageResource(R.drawable.ic_arrow_dropdown); + root.findViewById(R.id.file_view_description_area).setVisibility(View.GONE); + ((TextView) root.findViewById(R.id.file_view_title)).setText(claim.getTitle()); + ((TextView) root.findViewById(R.id.file_view_description)).setText(claim.getDescription()); + ((TextView) root.findViewById(R.id.file_view_publisher_name)).setText( + Helper.isNullOrEmpty(claim.getPublisherName()) ? getString(R.string.anonymous) : claim.getPublisherName()); - getView().findViewById(R.id.file_view_description_area).setVisibility(View.GONE); - ((TextView) getView().findViewById(R.id.file_view_title)).setText(claim.getTitle()); - ((TextView) getView().findViewById(R.id.file_view_description)).setText(claim.getDescription()); - ((TextView) getView().findViewById(R.id.file_view_publisher_name)).setText( - Helper.isNullOrEmpty(claim.getPublisherName()) ? getString(R.string.anonymous) : claim.getPublisherName()); + Context context = getContext(); + RecyclerView descTagsList = root.findViewById(R.id.file_view_tag_list); + FlexboxLayoutManager flm = new FlexboxLayoutManager(context); + descTagsList.setLayoutManager(flm); - Context context = getContext(); - RecyclerView descTagsList = getView().findViewById(R.id.file_view_tag_list); - FlexboxLayoutManager flm = new FlexboxLayoutManager(context); - descTagsList.setLayoutManager(flm); - - List tags = claim.getTagObjects(); - TagListAdapter tagListAdapter = new TagListAdapter(tags, context); - tagListAdapter.setClickListener(new TagListAdapter.TagClickListener() { - @Override - public void onTagClicked(Tag tag, int customizeMode) { - if (customizeMode == TagListAdapter.CUSTOMIZE_MODE_NONE) { - Context ctx = getContext(); - if (ctx instanceof MainActivity) { - ((MainActivity) ctx).openAllContentFragmentWithTag(tag.getName()); + List tags = claim.getTagObjects(); + TagListAdapter tagListAdapter = new TagListAdapter(tags, context); + tagListAdapter.setClickListener(new TagListAdapter.TagClickListener() { + @Override + public void onTagClicked(Tag tag, int customizeMode) { + if (customizeMode == TagListAdapter.CUSTOMIZE_MODE_NONE) { + Context ctx = getContext(); + if (ctx instanceof MainActivity) { + ((MainActivity) ctx).openAllContentFragmentWithTag(tag.getName()); + } } } + }); + descTagsList.setAdapter(tagListAdapter); + root.findViewById(R.id.file_view_tag_area).setVisibility(tags.size() > 0 ? View.VISIBLE : View.GONE); + + root.findViewById(R.id.file_view_exoplayer_container).setVisibility(View.GONE); + root.findViewById(R.id.file_view_unsupported_container).setVisibility(View.GONE); + root.findViewById(R.id.file_view_media_meta_container).setVisibility(View.VISIBLE); + + Claim.GenericMetadata metadata = claim.getValue(); + if (!Helper.isNullOrEmpty(claim.getThumbnailUrl())) { + ImageView thumbnailView = root.findViewById(R.id.file_view_thumbnail); + Glide.with(getContext().getApplicationContext()).load(claim.getThumbnailUrl()).centerCrop().into(thumbnailView); + } else { + // display first x letters of claim name, with random background } - }); - descTagsList.setAdapter(tagListAdapter); - getView().findViewById(R.id.file_view_tag_area).setVisibility(tags.size() > 0 ? View.VISIBLE : View.GONE); - getView().findViewById(R.id.file_view_exoplayer_container).setVisibility(View.GONE); - getView().findViewById(R.id.file_view_unsupported_container).setVisibility(View.GONE); - getView().findViewById(R.id.file_view_media_meta_container).setVisibility(View.VISIBLE); - - Claim.GenericMetadata metadata = claim.getValue(); - if (!Helper.isNullOrEmpty(claim.getThumbnailUrl())) { - ImageView thumbnailView = getView().findViewById(R.id.file_view_thumbnail); - Glide.with(getContext().getApplicationContext()).load(claim.getThumbnailUrl()).centerCrop().into(thumbnailView); - } else { - // display first x letters of claim name, with random background - } - - getView().findViewById(R.id.file_view_main_action_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onMainActionButtonClicked(); - } - }); - getView().findViewById(R.id.file_view_media_meta_container).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onMainActionButtonClicked(); - } - }); - - if (metadata instanceof Claim.StreamMetadata) { - Claim.StreamMetadata streamMetadata = (Claim.StreamMetadata) metadata; - long publishTime = streamMetadata.getReleaseTime() > 0 ? streamMetadata.getReleaseTime() * 1000 : claim.getTimestamp() * 1000; - ((TextView) getView().findViewById(R.id.file_view_publish_time)).setText(DateUtils.getRelativeTimeSpanString( - publishTime, System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE)); - - Fee fee = streamMetadata.getFee(); - if (fee != null && Helper.parseDouble(fee.getAmount(), 0) > 0) { - getView().findViewById(R.id.file_view_fee_container).setVisibility(View.VISIBLE); - ((TextView) getView().findViewById(R.id.file_view_fee)).setText( - Helper.shortCurrencyFormat(claim.getActualCost(Lbryio.LBCUSDRate).doubleValue())); - } - } - - getView().findViewById(R.id.file_view_icon_follow_unfollow).setVisibility(claim.getSigningChannel() != null ? View.VISIBLE : View.GONE); - - MaterialButton mainActionButton = getView().findViewById(R.id.file_view_main_action_button); - if (claim.isPlayable()) { - mainActionButton.setText(R.string.play); - } else if (claim.isViewable()) { - mainActionButton.setText(R.string.view); - } else { - mainActionButton.setText(R.string.download); - } - - if (claim.isFree()) { - if (claim.isPlayable()) { - if (MainActivity.nowPlayingClaim != null && MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(claim.getClaimId())) { - // claim already playing - showExoplayerView(); - playMedia(); - } else { + root.findViewById(R.id.file_view_main_action_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { onMainActionButtonClicked(); } - } else if (claim.isViewable() && Lbry.SDK_READY) { - onMainActionButtonClicked(); - } else if (!Lbry.SDK_READY) { + }); + root.findViewById(R.id.file_view_media_meta_container).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + onMainActionButtonClicked(); + } + }); + + if (metadata instanceof Claim.StreamMetadata) { + Claim.StreamMetadata streamMetadata = (Claim.StreamMetadata) metadata; + long publishTime = streamMetadata.getReleaseTime() > 0 ? streamMetadata.getReleaseTime() * 1000 : claim.getTimestamp() * 1000; + ((TextView) root.findViewById(R.id.file_view_publish_time)).setText(DateUtils.getRelativeTimeSpanString( + publishTime, System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE)); + + Fee fee = streamMetadata.getFee(); + if (fee != null && Helper.parseDouble(fee.getAmount(), 0) > 0) { + root.findViewById(R.id.file_view_fee_container).setVisibility(View.VISIBLE); + ((TextView) root.findViewById(R.id.file_view_fee)).setText( + Helper.shortCurrencyFormat(claim.getActualCost(Lbryio.LBCUSDRate).doubleValue())); + } + } + + root.findViewById(R.id.file_view_icon_follow_unfollow).setVisibility(claim.getSigningChannel() != null ? View.VISIBLE : View.GONE); + + MaterialButton mainActionButton = root.findViewById(R.id.file_view_main_action_button); + if (claim.isPlayable()) { + mainActionButton.setText(R.string.play); + } else if (claim.isViewable()) { + mainActionButton.setText(R.string.view); + } else { + mainActionButton.setText(R.string.download); + } + + if (claim.isFree()) { + if (claim.isPlayable()) { + if (MainActivity.nowPlayingClaim != null && MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(claim.getClaimId())) { + // claim already playing + showExoplayerView(); + playMedia(); + } else { + onMainActionButtonClicked(); + } + } else if (claim.isViewable() && Lbry.SDK_READY) { + onMainActionButtonClicked(); + } else if (!Lbry.SDK_READY) { + restoreMainActionButton(); + } + } else { restoreMainActionButton(); } - } else { - restoreMainActionButton(); - } - RecyclerView relatedContentList = getView().findViewById(R.id.file_view_related_content_list); - if (relatedContentList == null || relatedContentList.getAdapter() == null || relatedContentList.getAdapter().getItemCount() == 0) { - loadRelatedContent(); + RecyclerView relatedContentList = root.findViewById(R.id.file_view_related_content_list); + if (relatedContentList == null || relatedContentList.getAdapter() == null || relatedContentList.getAdapter().getItemCount() == 0) { + loadRelatedContent(); + } } } @@ -1067,7 +1070,7 @@ public class FileViewFragment extends BaseFragment implements boolean newPlayerCreated = false; Context context = getContext(); - if (MainActivity.appPlayer == null) { + if (MainActivity.appPlayer == null && context != null) { MainActivity.appPlayer = new SimpleExoPlayer.Builder(context).build(); MainActivity.playerCache = new SimpleCache(context.getCacheDir(), new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 256), new ExoDatabaseProvider(context)); newPlayerCreated = true; @@ -1081,7 +1084,6 @@ public class FileViewFragment extends BaseFragment implements ((MainActivity) context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - if (MainActivity.nowPlayingClaim != null && MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(claim.getClaimId()) && !newPlayerCreated) { @@ -1089,23 +1091,25 @@ public class FileViewFragment extends BaseFragment implements return; } - showBuffering(); - if (fileViewPlayerListener != null) { - MainActivity.appPlayer.addListener(fileViewPlayerListener); - } - if (context instanceof MainActivity) { - ((MainActivity) context).setNowPlayingClaim(claim); - } + if (MainActivity.appPlayer != null) { + showBuffering(); + if (fileViewPlayerListener != null) { + MainActivity.appPlayer.addListener(fileViewPlayerListener); + } + if (context instanceof MainActivity) { + ((MainActivity) context).setNowPlayingClaim(claim); + } - MainActivity.appPlayer.setPlayWhenReady(true); - String userAgent = Util.getUserAgent(context, getString(R.string.app_name)); - String mediaSourceUrl = getStreamingUrl(); - MediaSource mediaSource = new ProgressiveMediaSource.Factory( - new CacheDataSourceFactory(MainActivity.playerCache, new DefaultDataSourceFactory(context, userAgent)), - new DefaultExtractorsFactory() - ).createMediaSource(Uri.parse(mediaSourceUrl)); + MainActivity.appPlayer.setPlayWhenReady(true); + String userAgent = Util.getUserAgent(context, getString(R.string.app_name)); + String mediaSourceUrl = getStreamingUrl(); + MediaSource mediaSource = new ProgressiveMediaSource.Factory( + new CacheDataSourceFactory(MainActivity.playerCache, new DefaultDataSourceFactory(context, userAgent)), + new DefaultExtractorsFactory() + ).createMediaSource(Uri.parse(mediaSourceUrl)); - MainActivity.appPlayer.prepare(mediaSource, true, true); + MainActivity.appPlayer.prepare(mediaSource, true, true); + } } private void setCurrentPlayer(Player currentPlayer) { @@ -1174,9 +1178,12 @@ public class FileViewFragment extends BaseFragment implements public void onSuccess(int count) { try { String displayText = getResources().getQuantityString(R.plurals.view_count, count, NumberFormat.getInstance().format(count)); - TextView textViewCount = getView().findViewById(R.id.file_view_view_count); - Helper.setViewText(textViewCount, displayText); - Helper.setViewVisibility(textViewCount, View.VISIBLE); + View root = getView(); + if (root != null) { + TextView textViewCount = root.findViewById(R.id.file_view_view_count); + Helper.setViewText(textViewCount, displayText); + Helper.setViewVisibility(textViewCount, View.VISIBLE); + } } catch (IllegalStateException ex) { // pass } @@ -1218,7 +1225,12 @@ public class FileViewFragment extends BaseFragment implements if (claim != null) { Fee fee = ((Claim.StreamMetadata) claim.getValue()).getFee(); double cost = claim.getActualCost(Lbryio.LBCUSDRate).doubleValue(); - String message = getResources().getQuantityString(R.plurals.confirm_purchase_message, cost == 1 ? 1 : 2, claim.getTitle(), cost); + String formattedCost = Helper.LBC_CURRENCY_FORMAT.format(cost); + String message = getResources().getQuantityString( + R.plurals.confirm_purchase_message, + cost == 1 ? 1 : 2, + claim.getTitle(), + formattedCost.equals("0") ? Helper.FULL_LBC_CURRENCY_FORMAT.format(cost) : formattedCost); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()). setTitle(R.string.confirm_purchase). setMessage(message) @@ -1445,10 +1457,10 @@ public class FileViewFragment extends BaseFragment implements } public void showError(String message) { - Snackbar.make(getView().findViewById(R.id.file_view_claim_display_area), message, Snackbar.LENGTH_LONG). - setTextColor(Color.WHITE). - setBackgroundTint(Color.RED). - show(); + View root = getView(); + if (root != null) { + Snackbar.make(root, message, Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show(); + } } private void loadRelatedContent() { @@ -1884,4 +1896,9 @@ public class FileViewFragment extends BaseFragment implements // invalid file info for download } } + + @Override + public boolean shouldHideGlobalPlayer() { + return true; + } } diff --git a/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java b/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java index 089ca60..59988c9 100644 --- a/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java @@ -396,8 +396,12 @@ public class FollowingFragment extends BaseFragment implements } private Map buildSuggestedOptions() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); - boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false); + Context context = getContext(); + boolean canShowMatureContent = false; + if (context != null) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false); + } return Lbry.buildClaimSearchOptions( Claim.TYPE_CHANNEL, diff --git a/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java b/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java index 6b69ef1..e661d73 100644 --- a/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java @@ -46,6 +46,8 @@ import io.lbry.browser.model.NavMenuItem; import io.lbry.browser.model.ViewHistory; import io.lbry.browser.tasks.claim.AbandonChannelTask; import io.lbry.browser.tasks.claim.AbandonHandler; +import io.lbry.browser.tasks.claim.ClaimListResultHandler; +import io.lbry.browser.tasks.claim.ResolveTask; import io.lbry.browser.tasks.file.BulkDeleteFilesTask; import io.lbry.browser.tasks.file.DeleteFileTask; import io.lbry.browser.tasks.file.FileListTask; @@ -55,6 +57,7 @@ import io.lbry.browser.ui.channel.ChannelFormFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.LbryAnalytics; +import io.lbry.browser.utils.LbryUri; public class LibraryFragment extends BaseFragment implements ActionMode.Callback, DownloadActionListener, SelectionModeListener, SdkStatusListener { @@ -320,7 +323,10 @@ public class LibraryFragment extends BaseFragment implements } else { contentListAdapter.addItems(claims); } - contentList.setAdapter(contentListAdapter); + if (contentList.getAdapter() == null) { + contentList.setAdapter(contentListAdapter); + } + resolveMissingChannelNames(buildUrlsToResolve(claims)); checkListEmpty(); contentListLoading = false; } @@ -355,7 +361,9 @@ public class LibraryFragment extends BaseFragment implements } else { contentListAdapter.addItems(claims); } - contentList.setAdapter(contentListAdapter); + if (contentList.getAdapter() == null) { + contentList.setAdapter(contentListAdapter); + } checkListEmpty(); contentListLoading = false; } @@ -564,6 +572,50 @@ public class LibraryFragment extends BaseFragment implements } } + private List buildUrlsToResolve(List claims) { + List urls = new ArrayList<>(); + for (Claim claim : claims) { + Claim channel = claim.getSigningChannel(); + if (channel != null && Helper.isNullOrEmpty(channel.getName()) && !Helper.isNullOrEmpty(channel.getClaimId())) { + LbryUri uri = LbryUri.tryParse(String.format("%s#%s", claim.getName(), claim.getClaimId())); + if (uri != null) { + urls.add(uri.toString()); + } + } + } + return urls; + } + + private void resolveMissingChannelNames(List urls) { + if (urls.size() > 0) { + ResolveTask task = new ResolveTask(urls, Lbry.SDK_CONNECTION_STRING, null, new ClaimListResultHandler() { + @Override + public void onSuccess(List claims) { + boolean updated = false; + for (Claim claim : claims) { + if (claim.getClaimId() == null) { + continue; + } + + if (contentListAdapter != null) { + contentListAdapter.updateSigningChannelForClaim(claim); + updated = true; + } + } + if (updated) { + contentListAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onError(Exception error) { + + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + public void onEnterSelectionMode() { Context context = getContext(); if (context instanceof MainActivity) { diff --git a/app/src/main/java/io/lbry/browser/ui/publish/PublishFragment.java b/app/src/main/java/io/lbry/browser/ui/publish/PublishFragment.java new file mode 100644 index 0000000..48c45bb --- /dev/null +++ b/app/src/main/java/io/lbry/browser/ui/publish/PublishFragment.java @@ -0,0 +1,316 @@ +package io.lbry.browser.ui.publish; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.camera.core.Camera; +import androidx.camera.core.CameraSelector; +import androidx.camera.core.CameraX; +import androidx.camera.core.Preview; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.camera.view.PreviewView; +import androidx.core.content.ContextCompat; +import androidx.lifecycle.LifecycleOwner; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.snackbar.Snackbar; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import io.lbry.browser.MainActivity; +import io.lbry.browser.R; +import io.lbry.browser.adapter.GalleryGridAdapter; +import io.lbry.browser.listener.CameraPermissionListener; +import io.lbry.browser.listener.StoragePermissionListener; +import io.lbry.browser.model.GalleryItem; +import io.lbry.browser.tasks.localdata.LoadGalleryItemsTask; +import io.lbry.browser.ui.BaseFragment; +import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.LbryAnalytics; + +public class PublishFragment extends BaseFragment implements CameraPermissionListener, StoragePermissionListener { + + private boolean loadGalleryItemsPending; + private PreviewView cameraPreview; + private RecyclerView galleryGrid; + private GalleryGridAdapter adapter; + private TextView noVideosLoaded; + private View loading; + + private View buttonRecord; + private View buttonTakePhoto; + private View buttonUpload; + + private boolean recordPending; + private boolean takePhotoPending; + private ListenableFuture cameraProviderFuture; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.fragment_publish, container, false); + + noVideosLoaded = root.findViewById(R.id.publish_grid_no_videos); + loading = root.findViewById(R.id.publish_grid_loading); + cameraPreview = root.findViewById(R.id.publish_camera_preview); + + Context context = getContext(); + galleryGrid = root.findViewById(R.id.publish_video_grid); + GridLayoutManager glm = new GridLayoutManager(context, 3); + galleryGrid.setLayoutManager(glm); + galleryGrid.addItemDecoration(new GalleryGridAdapter.GalleryGridItemDecoration( + 3, Helper.getScaledValue(3, context.getResources().getDisplayMetrics().density))); + + buttonRecord = root.findViewById(R.id.publish_record_button); + buttonTakePhoto = root.findViewById(R.id.publish_photo_button); + buttonUpload = root.findViewById(R.id.publish_upload_button); + + buttonRecord.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + checkCameraPermissionAndRecord(); + } + }); + buttonTakePhoto.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + checkCameraPermissionAndTakePhoto(); + } + }); + + return root; + } + + private boolean cameraAvailable() { + Context context = getContext(); + return context != null && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); + } + + private void showCameraPreview() { + buttonRecord.setBackgroundColor(Color.TRANSPARENT); + buttonTakePhoto.setBackgroundColor(Color.TRANSPARENT); + displayPreviewWithCameraX(); + } + + private void displayPreviewWithCameraX() { + Context context = getContext(); + if (context != null) { + cameraProviderFuture = ProcessCameraProvider.getInstance(context); + cameraProviderFuture.addListener(new Runnable() { + @Override + public void run() { + try { + ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); + if (cameraProvider != null) { + Preview preview = new Preview.Builder().build(); + CameraSelector cameraSelector = new CameraSelector.Builder() + .requireLensFacing(CameraSelector.LENS_FACING_BACK) + .build(); + + Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner) context, cameraSelector, preview); + preview.setSurfaceProvider(cameraPreview.createSurfaceProvider(camera.getCameraInfo())); + } else { + android.util.Log.d("#HELP", "camera provider future is null?"); + } + } catch (ExecutionException | InterruptedException ex) { + // pass + } + } + }, ContextCompat.getMainExecutor(context)); + } + } + + private void checkCameraPermissionAndRecord() { + Context context = getContext(); + if (!MainActivity.hasPermission(Manifest.permission.CAMERA, context)) { + recordPending = true; + MainActivity.requestPermission( + Manifest.permission.CAMERA, + MainActivity.REQUEST_CAMERA_PERMISSION, + getString(R.string.camera_permission_rationale_record), + context, + true); + } else { + // start video record intent + } + } + + private void checkCameraPermissionAndTakePhoto() { + Context context = getContext(); + if (!MainActivity.hasPermission(Manifest.permission.CAMERA, context)) { + takePhotoPending = true; + MainActivity.requestPermission( + Manifest.permission.CAMERA, + MainActivity.REQUEST_CAMERA_PERMISSION, + getString(R.string.camera_permission_rationale_photo), + context, + true); + } else { + // start video record intent + } + } + + public void onResume() { + super.onResume(); + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Publish", "Publish"); + activity.addCameraPermissionListener(this); + activity.addStoragePermissionListener(this); + activity.hideFloatingWalletBalance(); + + + if (cameraAvailable() && MainActivity.hasPermission(Manifest.permission.CAMERA, context)) { + showCameraPreview(); + } + } + + checkStoragePermissionAndLoadVideos(); + } + + @SuppressLint("RestrictedApi") + public void onStop() { + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + activity.removeCameraPermissionListener(this); + activity.removeStoragePermissionListener(this); + activity.showFloatingWalletBalance(); + } + CameraX.unbindAll(); + super.onStop(); + } + + private void checkStoragePermissionAndLoadVideos() { + Context context = getContext(); + if (MainActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context)) { + loadGalleryItems(); + } else { + loadGalleryItemsPending = true; + MainActivity.requestPermission( + Manifest.permission.WRITE_EXTERNAL_STORAGE, + MainActivity.REQUEST_STORAGE_PERMISSION, + getString(R.string.storage_permission_rationale_download), + context, + true); + } + } + + private void loadGalleryItems() { + Context context = getContext(); + Helper.setViewVisibility(noVideosLoaded, View.GONE); + LoadGalleryItemsTask task = new LoadGalleryItemsTask(loading, context, new LoadGalleryItemsTask.LoadGalleryHandler() { + @Override + public void onItemLoaded(GalleryItem item) { + if (context != null) { + if (adapter == null) { + adapter = new GalleryGridAdapter(Arrays.asList(item), context); + } else { + adapter.addItem(item); + } + + if (galleryGrid.getAdapter() == null) { + galleryGrid.setAdapter(adapter); + } + Helper.setViewVisibility(loading, adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } + } + + @Override + public void onAllItemsLoaded(List items) { + if (context != null) { + if (adapter == null) { + adapter = new GalleryGridAdapter(items, context); + } else { + adapter.addItems(items); + } + + if (galleryGrid.getAdapter() == null) { + galleryGrid.setAdapter(adapter); + } + } + checkNoVideosLoaded(); + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public void checkNoVideosLoaded() { + Helper.setViewVisibility(noVideosLoaded, adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } + + @Override + public void onCameraPermissionGranted() { + if (recordPending) { + // record video + recordPending = false; + } else if (takePhotoPending) { + // take a photo + takePhotoPending = false; + } + } + + @Override + public void onCameraPermissionRefused() { + if (takePhotoPending) { + takePhotoPending = false; + Snackbar.make(getView(), R.string.camera_permission_rationale_photo, Toast.LENGTH_LONG). + setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show(); + return; + } + + recordPending = false; + Snackbar.make(getView(), R.string.camera_permission_rationale_record, Toast.LENGTH_LONG). + setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show(); + } + + @Override + public void onRecordAudioPermissionGranted() { + + } + + @Override + public void onRecordAudioPermissionRefused() { + + } + + @Override + public void onStoragePermissionGranted() { + if (loadGalleryItemsPending) { + loadGalleryItemsPending = false; + loadGalleryItems(); + } + } + + @Override + public void onStoragePermissionRefused() { + Snackbar.make(getView(), R.string.storage_permission_rationale_videos, Snackbar.LENGTH_LONG).setBackgroundTint( + ContextCompat.getColor(getContext(), R.color.red) + ).show(); + } + + @Override + public boolean shouldHideGlobalPlayer() { + return true; + } + + @Override + public boolean shouldSuspendGlobalPlayer() { + return true; + } +} diff --git a/app/src/main/java/io/lbry/browser/ui/publish/PublishesFragment.java b/app/src/main/java/io/lbry/browser/ui/publish/PublishesFragment.java new file mode 100644 index 0000000..3420f5f --- /dev/null +++ b/app/src/main/java/io/lbry/browser/ui/publish/PublishesFragment.java @@ -0,0 +1,321 @@ +package io.lbry.browser.ui.publish; + +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Color; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ProgressBar; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.view.ActionMode; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.lbry.browser.MainActivity; +import io.lbry.browser.R; +import io.lbry.browser.adapter.ClaimListAdapter; +import io.lbry.browser.listener.SdkStatusListener; +import io.lbry.browser.listener.SelectionModeListener; +import io.lbry.browser.model.Claim; +import io.lbry.browser.tasks.claim.AbandonChannelTask; +import io.lbry.browser.tasks.claim.AbandonHandler; +import io.lbry.browser.tasks.claim.ClaimListResultHandler; +import io.lbry.browser.tasks.claim.ClaimListTask; +import io.lbry.browser.ui.BaseFragment; +import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; + +public class PublishesFragment extends BaseFragment implements ActionMode.Callback, SelectionModeListener, SdkStatusListener { + + private Button buttonNewPublish; + private FloatingActionButton fabNewPublish; + private ActionMode actionMode; + private View emptyView; + private View layoutSdkInitializing; + private ProgressBar loading; + private ProgressBar bigLoading; + private RecyclerView contentList; + private ClaimListAdapter adapter; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.fragment_publishes, container, false); + + buttonNewPublish = root.findViewById(R.id.publishes_create_button); + fabNewPublish = root.findViewById(R.id.publishes_fab_new_publish); + buttonNewPublish.setOnClickListener(newPublishClickListener); + fabNewPublish.setOnClickListener(newPublishClickListener); + + emptyView = root.findViewById(R.id.publishes_empty_container); + layoutSdkInitializing = root.findViewById(R.id.container_sdk_initializing); + contentList = root.findViewById(R.id.publishes_list); + LinearLayoutManager llm = new LinearLayoutManager(getContext()); + contentList.setLayoutManager(llm); + loading = root.findViewById(R.id.publishes_list_loading); + bigLoading = root.findViewById(R.id.publishes_list_big_loading); + + layoutSdkInitializing.setVisibility(Lbry.SDK_READY ? View.GONE : View.VISIBLE); + + return root; + } + + private View.OnClickListener newPublishClickListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + Context context = getContext(); + if (context instanceof MainActivity) { + //((MainActivity) context).openPublishForm(null); + } + } + }; + + @Override + public void onStart() { + super.onStart(); + Context context = getContext(); + if (context != null) { + MainActivity activity = (MainActivity) context; + activity.hideFloatingWalletBalance(); + } + } + + @Override + public void onStop() { + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + activity.showFloatingWalletBalance(); + } + super.onStop(); + } + + public void onResume() { + super.onResume(); + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + activity.setWunderbarValue(null); + LbryAnalytics.setCurrentScreen(activity, "Publishes", "Publishes"); + } + + if (!Lbry.SDK_READY) { + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + activity.addSdkStatusListener(this); + } + } else { + onSdkReady(); + } + } + + public void onSdkReady() { + Helper.setViewVisibility(layoutSdkInitializing, View.GONE); + Helper.setViewVisibility(fabNewPublish, View.VISIBLE); + if (adapter != null && contentList != null) { + contentList.setAdapter(adapter); + } + fetchPublishes(); + } + + public View getLoading() { + return (adapter == null || adapter.getItemCount() == 0) ? bigLoading : loading; + } + + private void checkNoPublishes() { + Helper.setViewVisibility(emptyView, adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } + + private void fetchPublishes() { + Helper.setViewVisibility(emptyView, View.GONE); + ClaimListTask task = new ClaimListTask(Arrays.asList(Claim.TYPE_STREAM, Claim.TYPE_REPOST), getLoading(), new ClaimListResultHandler() { + @Override + public void onSuccess(List claims) { + Lbry.ownClaims = Helper.filterDeletedClaims(new ArrayList<>(claims)); + Context context = getContext(); + if (adapter == null) { + adapter = new ClaimListAdapter(claims, context); + adapter.setCanEnterSelectionMode(true); + adapter.setSelectionModeListener(PublishesFragment.this); + adapter.setListener(new ClaimListAdapter.ClaimListItemListener() { + @Override + public void onClaimClicked(Claim claim) { + if (context instanceof MainActivity) { + ((MainActivity) context).openFileClaim(claim); + } + } + }); + if (contentList != null) { + contentList.setAdapter(adapter); + } + } else { + adapter.setItems(claims); + } + + checkNoPublishes(); + } + + @Override + public void onError(Exception error) { + checkNoPublishes(); + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public void onEnterSelectionMode() { + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + activity.startSupportActionMode(this); + } + } + public void onItemSelectionToggled() { + if (actionMode != null) { + actionMode.setTitle(String.valueOf(adapter.getSelectedCount())); + actionMode.invalidate(); + } + } + public void onExitSelectionMode() { + if (actionMode != null) { + actionMode.finish(); + } + } + + @Override + public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { + this.actionMode = actionMode; + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + if (!activity.isDarkMode()) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + + actionMode.getMenuInflater().inflate(R.menu.menu_claim_list, menu); + return true; + } + @Override + public void onDestroyActionMode(ActionMode actionMode) { + if (adapter != null) { + adapter.clearSelectedItems(); + adapter.setInSelectionMode(false); + adapter.notifyDataSetChanged(); + } + Context context = getContext(); + if (context != null) { + MainActivity activity = (MainActivity) context; + if (!activity.isDarkMode()) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + this.actionMode = null; + } + + @Override + public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode actionMode, Menu menu) { + int selectionCount = adapter != null ? adapter.getSelectedCount() : 0; + menu.findItem(R.id.action_edit).setVisible(selectionCount == 1); + return true; + } + + @Override + public boolean onActionItemClicked(androidx.appcompat.view.ActionMode actionMode, MenuItem menuItem) { + if (R.id.action_edit == menuItem.getItemId()) { + if (adapter != null && adapter.getSelectedCount() > 0) { + Claim claim = adapter.getSelectedItems().get(0); + // start channel editor with the claim + Context context = getContext(); + if (context instanceof MainActivity) { + Map params = new HashMap<>(); + params.put("claim", claim); + /*((MainActivity) context).openFragment(PublishFormFragment.class, true, NavMenuItem.ID_ITEM_NEW_PUBLISH, params);*/ + } + + actionMode.finish(); + return true; + } + } + if (R.id.action_delete == menuItem.getItemId()) { + if (adapter != null && adapter.getSelectedCount() > 0) { + final List selectedClaims = new ArrayList<>(adapter.getSelectedItems()); + String message = getResources().getQuantityString(R.plurals.confirm_delete_publishes, selectedClaims.size()); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()). + setTitle(R.string.delete_selection). + setMessage(message) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + handleDeleteSelectedClaims(selectedClaims); + } + }).setNegativeButton(R.string.no, null); + builder.show(); + return true; + } + } + + return false; + } + + private void handleDeleteSelectedClaims(List selectedClaims) { + List claimIds = new ArrayList<>(); + + for (Claim claim : selectedClaims) { + claimIds.add(claim.getClaimId()); + } + + if (actionMode != null) { + actionMode.finish(); + } + + Helper.setViewVisibility(contentList, View.INVISIBLE); + Helper.setViewVisibility(fabNewPublish, View.INVISIBLE); + AbandonChannelTask task = new AbandonChannelTask(claimIds, bigLoading, new AbandonHandler() { + @Override + public void onComplete(List successfulClaimIds, List failedClaimIds, List errors) { + View root = getView(); + if (root != null) { + if (failedClaimIds.size() > 0) { + Snackbar.make(root, R.string.one_or_more_publishes_failed_abandon, Snackbar.LENGTH_LONG). + setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show(); + } else if (successfulClaimIds.size() == claimIds.size()) { + try { + String message = getResources().getQuantityString(R.plurals.publishes_deleted, successfulClaimIds.size()); + Snackbar.make(root, message, Snackbar.LENGTH_LONG).show(); + } catch (IllegalStateException ex) { + // pass + } + } + } + + Lbry.abandonedClaimIds.addAll(successfulClaimIds); + if (adapter != null) { + adapter.setItems(Helper.filterDeletedClaims(adapter.getItems())); + } + + Helper.setViewVisibility(contentList, View.VISIBLE); + Helper.setViewVisibility(fabNewPublish, View.VISIBLE); + checkNoPublishes(); + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } +} diff --git a/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java b/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java index bfbfe29..6f9a09b 100644 --- a/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java @@ -214,8 +214,13 @@ public class SearchFragment extends BaseFragment implements } searchLoading = true; - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); - boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false); + Context context = getContext(); + boolean canShowMatureContent = false; + if (context != null) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false); + } + LighthouseSearchTask task = new LighthouseSearchTask( currentQuery, PAGE_SIZE, currentFrom, canShowMatureContent, null, loadingView, new ClaimSearchTask.ClaimSearchResultHandler() { @Override diff --git a/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java b/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java index 3851969..b0b2791 100644 --- a/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java @@ -233,7 +233,7 @@ public class RewardsFragment extends BaseFragment implements RewardListAdapter.R Helper.shortCurrencyFormat(Lbryio.totalUnclaimedRewardAmount)); double unclaimedRewardAmountUsd = Lbryio.totalUnclaimedRewardAmount * Lbryio.LBCUSDRate; Helper.setViewText(textAccountDriverTitle, accountDriverTitle); - Helper.setViewText(textFreeCreditsWorth, getString(R.string.free_credits_worth, Helper.USD_CURRENCY_FORMAT.format(unclaimedRewardAmountUsd))); + Helper.setViewText(textFreeCreditsWorth, getString(R.string.free_credits_worth, Helper.SIMPLE_CURRENCY_FORMAT.format(unclaimedRewardAmountUsd))); } catch (IllegalStateException ex) { // pass } diff --git a/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java b/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java index c435e05..c1d035f 100644 --- a/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java @@ -398,8 +398,12 @@ public class WalletFragment extends BaseFragment implements SdkStatusListener, W } private void checkReceiveAddress() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); - String receiveAddress = sp.getString(MainActivity.PREFERENCE_KEY_INTERNAL_WALLET_RECEIVE_ADDRESS, null); + Context context = getContext(); + String receiveAddress = null; + if (context != null) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + receiveAddress = sp.getString(MainActivity.PREFERENCE_KEY_INTERNAL_WALLET_RECEIVE_ADDRESS, null); + } if (Helper.isNullOrEmpty(receiveAddress)) { if (Lbry.SDK_READY) { generateNewAddress(); @@ -505,15 +509,16 @@ public class WalletFragment extends BaseFragment implements SdkStatusListener, W double tipsBalance = walletBalance.getTips().doubleValue(); double tipsUsdBalance = tipsBalance * Lbryio.LBCUSDRate; - Helper.setViewText(textWalletBalance, Helper.LBC_CURRENCY_FORMAT.format(balance)); + String formattedBalance = Helper.SIMPLE_CURRENCY_FORMAT.format(balance); + Helper.setViewText(textWalletBalance, balance > 0 && formattedBalance.equals("0") ? Helper.FULL_LBC_CURRENCY_FORMAT.format(balance) : formattedBalance); Helper.setViewText(textTipsBalance, Helper.shortCurrencyFormat(tipsBalance)); Helper.setViewText(textClaimsBalance, Helper.shortCurrencyFormat(walletBalance.getClaims().doubleValue())); Helper.setViewText(textSupportsBalance, Helper.shortCurrencyFormat(walletBalance.getSupports().doubleValue())); Helper.setViewText(textWalletInlineBalance, Helper.shortCurrencyFormat(balance)); if (Lbryio.LBCUSDRate > 0) { // only update display usd values if the rate is loaded - Helper.setViewText(textWalletBalanceUSD, String.format("≈$%s", Helper.USD_CURRENCY_FORMAT.format(usdBalance))); - Helper.setViewText(textTipsBalanceUSD, String.format("≈$%s", Helper.USD_CURRENCY_FORMAT.format(tipsUsdBalance))); + Helper.setViewText(textWalletBalanceUSD, String.format("≈$%s", Helper.SIMPLE_CURRENCY_FORMAT.format(usdBalance))); + Helper.setViewText(textTipsBalanceUSD, String.format("≈$%s", Helper.SIMPLE_CURRENCY_FORMAT.format(tipsUsdBalance))); } } } diff --git a/app/src/main/java/io/lbry/browser/utils/Helper.java b/app/src/main/java/io/lbry/browser/utils/Helper.java index b1e5434..edca68f 100644 --- a/app/src/main/java/io/lbry/browser/utils/Helper.java +++ b/app/src/main/java/io/lbry/browser/utils/Helper.java @@ -67,7 +67,8 @@ public final class Helper { public static final String LBC_CURRENCY_FORMAT_PATTERN = "#,###.##"; public static final String FILE_SIZE_FORMAT_PATTERN = "#,###.#"; public static final DecimalFormat LBC_CURRENCY_FORMAT = new DecimalFormat(LBC_CURRENCY_FORMAT_PATTERN); - public static final DecimalFormat USD_CURRENCY_FORMAT = new DecimalFormat("#,##0.00"); + public static final DecimalFormat FULL_LBC_CURRENCY_FORMAT = new DecimalFormat("#,###.########"); + public static final DecimalFormat SIMPLE_CURRENCY_FORMAT = new DecimalFormat("#,##0.00"); public static final String EXPLORER_TX_PREFIX = "https://explorer.lbry.com/tx"; public static boolean isNull(String value) { @@ -300,7 +301,7 @@ public final class Helper { return "0"; } - return format.format(value); + return format.format(value).equals("0") ? FULL_LBC_CURRENCY_FORMAT.format(value) : format.format(value); } public static String getValue(CharSequence cs) { @@ -680,4 +681,8 @@ public final class Helper { } return channelName; } + + public static int getScaledValue(int value, float scale) { + return (int) (value * scale + 0.5f); + } } diff --git a/app/src/main/java/io/lbry/browser/utils/Lbryio.java b/app/src/main/java/io/lbry/browser/utils/Lbryio.java index 2a180c1..85ed74e 100644 --- a/app/src/main/java/io/lbry/browser/utils/Lbryio.java +++ b/app/src/main/java/io/lbry/browser/utils/Lbryio.java @@ -44,7 +44,7 @@ import okhttp3.Response; public final class Lbryio { // TODO: Get this from the bundled aar - public static String SDK_VERSION = "0.73.1"; + public static String SDK_VERSION = "0.74.0"; public static User currentUser; public static boolean userHasSyncedWallet = false; @@ -121,7 +121,7 @@ public final class Lbryio { if (options != null) { for (Map.Entry option : options.entrySet()) { - qs.append(delim).append(option.getKey()).append("=").append(URLEncoder.encode(option.getValue(), "UTF8")); + qs.append(delim).append(option.getKey()).append("=").append(URLEncoder.encode(Helper.isNull(option.getValue()) ? "" : option.getValue(), "UTF8")); delim = "&"; } } diff --git a/app/src/main/res/drawable-anydpi/ic_photo.xml b/app/src/main/res/drawable-anydpi/ic_photo.xml new file mode 100644 index 0000000..e234376 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_photo.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi/ic_record.xml b/app/src/main/res/drawable-anydpi/ic_record.xml new file mode 100644 index 0000000..42f62d5 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_record.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_upload.xml b/app/src/main/res/drawable-anydpi/ic_upload.xml new file mode 100644 index 0000000..faebc4a --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_upload.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_photo.png b/app/src/main/res/drawable-hdpi/ic_photo.png new file mode 100644 index 0000000..b94ccbd Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_photo.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_record.png b/app/src/main/res/drawable-hdpi/ic_record.png new file mode 100644 index 0000000..6b81b67 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_record.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_upload.png b/app/src/main/res/drawable-hdpi/ic_upload.png new file mode 100644 index 0000000..6edf2fd Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_upload.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_photo.png b/app/src/main/res/drawable-mdpi/ic_photo.png new file mode 100644 index 0000000..668c633 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_photo.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_record.png b/app/src/main/res/drawable-mdpi/ic_record.png new file mode 100644 index 0000000..eb6412b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_record.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_upload.png b/app/src/main/res/drawable-mdpi/ic_upload.png new file mode 100644 index 0000000..c914b14 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_upload.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_photo.png b/app/src/main/res/drawable-xhdpi/ic_photo.png new file mode 100644 index 0000000..5165e58 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_photo.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_record.png b/app/src/main/res/drawable-xhdpi/ic_record.png new file mode 100644 index 0000000..f8c501c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_record.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_upload.png b/app/src/main/res/drawable-xhdpi/ic_upload.png new file mode 100644 index 0000000..904b34e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_upload.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_photo.png b/app/src/main/res/drawable-xxhdpi/ic_photo.png new file mode 100644 index 0000000..9a0ecd7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_photo.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_record.png b/app/src/main/res/drawable-xxhdpi/ic_record.png new file mode 100644 index 0000000..256cc26 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_record.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_upload.png b/app/src/main/res/drawable-xxhdpi/ic_upload.png new file mode 100644 index 0000000..4416762 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_upload.png differ diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml index 93a043b..44fd8b9 100644 --- a/app/src/main/res/layout/app_bar_main.xml +++ b/app/src/main/res/layout/app_bar_main.xml @@ -40,6 +40,7 @@ android:layout_height="wrap_content" android:hint="@string/uri_placeholder" android:imeOptions="actionGo" + android:inputType="textNoSuggestions" android:selectAllOnFocus="true" android:singleLine="true" android:textFontWeight="300" diff --git a/app/src/main/res/layout/fragment_publish.xml b/app/src/main/res/layout/fragment_publish.xml new file mode 100644 index 0000000..fcc1983 --- /dev/null +++ b/app/src/main/res/layout/fragment_publish.xml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_publishes.xml b/app/src/main/res/layout/fragment_publishes.xml new file mode 100644 index 0000000..e3af5bb --- /dev/null +++ b/app/src/main/res/layout/fragment_publishes.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_gallery.xml b/app/src/main/res/layout/list_item_gallery.xml new file mode 100644 index 0000000..d18d75e --- /dev/null +++ b/app/src/main/res/layout/list_item_gallery.xml @@ -0,0 +1,28 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8f93dac..d35fef9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,6 +15,7 @@ #55000000 #F4E866 + #333333 #33000000 @color/nextLbryGreen @color/nextLbryGreen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db9bc4e..698db21 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,6 +93,27 @@ %1$s followers + + Record + Take a Photo + Upload a file + We could not find any videos on your device. Take a photo or record a video to get started. + Please wait while we load your videos... + LBRY requires access to be able to display and publish your videos, images and other files from your device. + LBRY requires access to your camera to record videos. + LBRY requires access to your camera to take photos. + + + One or more content items could not be deleted at this time. Please try again later. + + Are you sure you want to delete the selected content item? + Are you sure you want to delete the selected content items? + + + The content item was successfully deleted. + The content items were successfully deleted. + + Oops! Something went wrong. Loaded Installation ID. @@ -323,7 +344,7 @@ The claim is pending publish on the blockchain. You will be able to access or edit the claim in a few moments. Pending Create - One or moe channels could not be deleted at this time. Please try again later. + One or more channels could not be deleted at this time. Please try again later. A minimum deposit of %1$s credit is required. A minimum deposit of %1$s credits is required. diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index a241c71..9999c44 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -1,8 +1,7 @@ + xmlns:app="http://schemas.android.com/apk/res-auto">