Related
My app is building successfully but when I open it it closes by itself...
I was able to successfully implement In-App-Review! But Update is failing and crashing my app on start.
I have an error in AppUpdateManager. I reproduced the code from scratch line by line... And the app crashed on its own, after build, when I called the App Update Manager:
private final AppUpdateManager mAppUpdateManager = AppUpdateManagerFactory.create(this);
Lib to Implement: https://developer.android.com/guide/playcore/in-app-updates
My MainActivity
package com.baraokeskina;
import com.baraokeskina.ReviewsApp.ReviewsManager;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.google.android.gms.tasks.Task;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.play.core.appupdate.AppUpdateInfo;
import com.google.android.play.core.appupdate.AppUpdateManager;
import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
import com.google.android.play.core.install.InstallStateUpdatedListener;
import com.google.android.play.core.install.model.AppUpdateType;
import com.google.android.play.core.install.model.InstallStatus;
import com.google.android.play.core.install.model.UpdateAvailability;
import com.google.android.play.core.review.ReviewInfo;
import com.google.android.play.core.review.ReviewManager;
import com.google.android.play.core.review.ReviewManagerFactory;
import com.swmansion.reanimated.BuildConfig;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
#Override
protected String getMainComponentName() {
return "BaraokeSkina";
}
private ReviewInfo reviewInfo;
private ReviewManager manager;
private static final int RC_APP_UPDATE = 22;
private final AppUpdateManager mAppUpdateManager = AppUpdateManagerFactory.create(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
activateUpdate();
if (new ReviewsManager().getDaysAppInstaled(this) >= 4) {
activateReviewInfo();
}
}
void activateUpdate(){
Task<AppUpdateInfo> appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
appUpdateInfoTask.addOnSuccessListener(result -> {
if (result.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& result.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
try {
mAppUpdateManager.startUpdateFlowForResult(result, AppUpdateType.FLEXIBLE, MainActivity.this, RC_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
System.out.println(e.getMessage());
}
}
});
mAppUpdateManager.registerListener(installStateUpdatedListener);
}
private final InstallStateUpdatedListener installStateUpdatedListener = installState -> {
if (installState.installStatus() == InstallStatus.DOWNLOADED) {
showCompleteUpdate();
}
};
#Override
protected void onStop() {
super.onStop();
if (mAppUpdateManager != null) {
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
}
}
private void showCompleteUpdate() {
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "Atualização pronta para ser Instalada...",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("Instalar", view -> mAppUpdateManager.completeUpdate());
snackbar.show();
}
void activateReviewInfo()
{
Activity context = this;
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(() -> {
manager = ReviewManagerFactory.create(context);
Task<ReviewInfo> request = manager.requestReviewFlow();
request.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
reviewInfo = task.getResult();
Task<Void> flow = manager.launchReviewFlow(context, reviewInfo);
flow.addOnCompleteListener(result -> {
});
}
});
request.addOnFailureListener(e -> System.out.println(e.getMessage()));
}, 15000);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE && resultCode != RESULT_OK) {
Toast.makeText(this, "Cancelado", Toast.LENGTH_SHORT).show();
}
}
/**
* Returns the instance of the {#link ReactActivityDelegate}. There the RootView is created and
* you can specify the rendered you wish to use (Fabric or the older renderer).
*/
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new MainActivityDelegate(this, getMainComponentName());
}
public static class MainActivityDelegate extends ReactActivityDelegate {
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
super(activity, mainComponentName);
}
#Override
protected ReactRootView createRootView() {
ReactRootView reactRootView = new ReactRootView(getContext());
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
return reactRootView;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
reviewInfo = null;
manager = null;
}
}
Bild.gradle implementations
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'com.facebook.android:facebook-android-sdk:[4,5)'
implementation 'com.google.android.material:material:1.1.0'
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:0.68.1!!" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
implementation project(':react-native-fbsdk-next')
implementation project(':react-native-linear-gradient')
implementation project(':react-native-svg')
implementation "androidx.multidex:multidex:2.0.1"
implementation project(':react-native-vector-icons')
implementation project(':watermelondb')
implementation project(':watermelondb-jsi')
implementation 'com.google.android.gms:play-services-tasks:18.0.2'
implementation project(path: ":#react-native-firebase_analytics")
implementation project(path: ":#react-native-firebase_inAppMessaging")
implementation 'com.google.android.play:review:2.0.1'
implementation 'com.google.android.play:app-update:2.0.1'
implementation 'com.google.android.play:asset-delivery:2.0.1'
implementation 'com.google.android.play:feature-delivery:2.0.1'
}
My Default Config - Build.gradle:
defaultConfig {
applicationId "com.baraokeskina"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 12
versionName "1.0.11"
multiDexEnabled true
resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
vectorDrawables.useSupportLibrary = true
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-21",
"APP_STL=c++_shared",
"NDK_TOOLCHAIN_VERSION=clang",
"GENERATED_SRC_DIR=$buildDir/generated/source",
"PROJECT_BUILD_DIR=$buildDir",
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build"
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
cppFlags "-std=c++17"
// Make sure this target name is the same you specify inside the
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
targets "baraokeskina_appmodules"
// Fix for windows limit on number of character in file paths and in command lines
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
arguments "NDK_OUT=${rootProject.projectDir.getParent()}\\.cxx",
"NDK_APP_SHORT_COMMANDS=true"
}
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
I Has resolved this question
The problem is
AppUpdateManagerFactory.create(this)
Only Can be instancied on one time.
I am trying to download blog features images form a WordPress blog page and then load them later.
Right now, every time the user scrolls down and up the image load again and again. Any idea...plzzz..I tried many solutions, for example, this one:
Preload multiple images with Glide
But, it didn't work.
I am using implementation 'com.github.bumptech.glide:glide:3.7.0'
And this is my code:
RecyclerViewAdapter
public void getthumbnail(String imageurl, final ImageView imageView, final int position){
ServiceWrapper serviceWrapper = new ServiceWrapper(null);
Call<GetThumbnail> call = serviceWrapper.getThumbnailCall(imageurl);
call.enqueue(new Callback<GetThumbnail>() {
#Override
public void onResponse(Call<GetThumbnail> call, Response<GetThumbnail> response) {
if (response.body() != null && response.isSuccessful()) {
try {
if (response.body().getMediaDetails()!=null){
// Log.e("recycler adapter", " image is here-- " + response.body().getMediaDetails().getSizes().getThumbnail().getSourceUrl());
// Log.e("Full IMG SIZE - ", " THIS IS FULL IMAGE URL-- " + response.body().getMediaDetails().getSizes().getFull().getSourceUrl());
imagepath.add(position, response.body().getMediaDetails().getSizes().getFull().getSourceUrl());
Glide.with(mContext)
.load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
}else {
}
}catch (Exception e){
// Log.e("adapter", "fail not media tag "+ e.toString());
}
}
}
#Override
public void onFailure(Call<GetThumbnail> call, Throwable t) {
// Log.e("adapter", " faile image "+t.toString());
}
});
}
UPDATE
I found this code and I put it on top of the other Glide code but it didn't work:
Glide.with(mContext)
.load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
.downloadOnly(new SimpleTarget<File>() {
#Override
public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {
}
});
UPDATE:
Now I used this code and it doesnt download the images faster:
Glide.with(mContext)
.load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
.downloadOnly(new SimpleTarget<File>() {
#Override
public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {
Glide.with(mContext)
.load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
}
});
Here is the complete ViewAdapter.java
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class RecyclerViewAdapter extends
RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<Model> dataset;
private Context mContext;
private ArrayList<Model> list;
private RecyclerViewAdapter adapter;
private RecyclerView recyclerView;
private LinearLayoutManager mLayoutManager;
public static List<WPPost> mListPost;
ArrayList<String> imagepath = new ArrayList<>();
private String baseURL = "https://www.myfitbytes.com/";
public RecyclerViewAdapter(ArrayList<Model> mlist, Context context) {
this.dataset = mlist;
this.mContext = context;
}
public static class ImageTypeViewHolder extends RecyclerView.ViewHolder{
TextView title, subtitle, date;
ImageView imageView;
CardView cardview;
public ImageTypeViewHolder(View itemView) {
super(itemView);
this.title = (TextView) itemView.findViewById(R.id.title);
//this.subtitle = (TextView) itemView.findViewById(R.id.subtitle);
this.date = (TextView) itemView.findViewById(R.id.date);
this.imageView = (ImageView) itemView.findViewById(R.id.Icon);
this.cardview = (CardView) itemView.findViewById(R.id.cardview);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from( parent.getContext()).inflate(R.layout.postdetails, parent, false);
return new ImageTypeViewHolder(view) ;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
final Model object = dataset.get(position);
// Log.d("RecyclerViewAdapter", "IMAGE="+object.Image);
imagepath.add(position, "");
if (Build.VERSION.SDK_INT >= 24)
{
//( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle , Html.FROM_HTML_MODE_LEGACY));
( (ImageTypeViewHolder) holder).title.setText( Html.fromHtml(object.title , Html.FROM_HTML_MODE_LEGACY) );
( (ImageTypeViewHolder) holder).date.setText( Html.fromHtml(object.date , Html.FROM_HTML_MODE_LEGACY) );
}
else
{
//( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle ));
( (ImageTypeViewHolder) holder).title.setText( Html.fromHtml(object.title ));
( (ImageTypeViewHolder) holder).date.setText( Html.fromHtml(object.date ));
}
( (ImageTypeViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, WPPostDetails.class);
intent.putExtra("itemPosition", position);
mContext.startActivity(intent);
}
});
try {
getthumbnail(object.Image, ( (ImageTypeViewHolder) holder).imageView, position);
}catch (Exception e){
// Log.e("adapter ","failed to get image "+e.toString() );
}
/// dataset.get(position)
}
public void getthumbnail(String imageurl, final ImageView imageView, final int position){
ServiceWrapper serviceWrapper = new ServiceWrapper(null);
Call<GetThumbnail> call = serviceWrapper.getThumbnailCall(imageurl);
call.enqueue(new Callback<GetThumbnail>() {
#Override
public void onResponse(Call<GetThumbnail> call, final Response<GetThumbnail> response) {
if (response.body() != null && response.isSuccessful()) {
try {
if (response.body().getMediaDetails()!=null){
// Log.e("recycler adapter", " image is here-- " + response.body().getMediaDetails().getSizes().getThumbnail().getSourceUrl());
// Log.e("Full IMG SIZE - ", " THIS IS FULL IMAGE URL-- " + response.body().getMediaDetails().getSizes().getFull().getSourceUrl());
imagepath.add(position, response.body().getMediaDetails().getSizes().getFull().getSourceUrl());
Glide.with(mContext)
.load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
.downloadOnly(new SimpleTarget<File>() {
#Override
public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {
Glide.with(mContext)
.load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
}
});
}else {
}
}catch (Exception e){
// Log.e("adapter", "fail not media tag "+ e.toString());
}
}
}
#Override
public void onFailure(Call<GetThumbnail> call, Throwable t) {
// Log.e("adapter", " faile image "+t.toString());
}
});
}
#Override
public int getItemCount() {
return dataset.size() ;
}
}
UPDATE
So, now that I tried to update glide 3 to 4.x I am getting the following in error in build.gradle.
All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes). Found versions 27.1.1, 27.1.0, 26.1.0. Examples include com.android.support:support-compat:27.1.1 and com.android.support:animated-vector-drawable:27.1.0 less... (⌘F1)
There are some combinations of libraries, or tools and libraries, that are incompatible, or can lead to bugs. One such incompatibility is compiling with a version of the Android support libraries that is not the latest version (or in particular, a version lower than your targetSdkVersion).
And the code is:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.myfitbytes"
minSdkVersion 14
targetSdkVersion 26
versionCode 3
versionName "3.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:26.1.0'
implementation 'com.android.support:support-v4:26.1.0'
//library for wordpress rest api
implementation 'com.android.support:cardview-v7:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.0.0-beta2'
implementation 'com.google.code.gson:gson:2.6.2'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
implementation 'com.squareup.okhttp:okhttp:2.4.0'
implementation 'com.squareup.okhttp3:okhttp:2.0.2'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
//implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
implementation 'com.android.volley:volley:1.0.0'
implementation 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
//Firebase
implementation 'com.google.firebase:firebase-core:16.0.4'
implementation 'com.google.firebase:firebase-messaging:17.3.3'
implementation 'org.apache.httpcomponents:httpcore:4.4.1'
//implementation 'org.apache.httpcomponents:httpclient:4.5.6'
//implementation group: 'org.apache.httpcomponents' , name: 'httpclient- android' , version: '4.3.5.1'
implementation files('libs/google-http-client-1.24.1.jar')
implementation files('libs/httpclient-4.5.3.jar')
//bottom nav
implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'
//picasso to download image from url faster
implementation 'com.squareup.picasso:picasso:2.71828'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
apply plugin: 'com.google.gms.google-services'
And, this line is in red:
implementation 'com.android.support:appcompat-v7:26.1.0'
The loading delay problem and image item disposition comes from onBindViewHolder method where you call a webservice in it. Everytime a list item is appeared in scrolling, onBindViewHolder is called to initialize the view with correct values. So you should not call a webservice here at all (but in the code it is called everytime). As Glide creates a queue of requests, it guarantees that the image would be downloaded only one time because it caches them. Also it guarantees that the image would be loaded if the ImageView is in visible area of the RecyclerView in scrolling process.
On the other hand, reaching the image url in your case needs two level of webservice api-s. So we should combine Glide procedure and retrieving the image url procedure to reach the best performance. Using Glide-OkHttp3-Integration library, I have developed this two level of asynchronous calls which makes the Glide aware of your data flow.
• Note that you should clean and rebuild your project to create GlideApp class in compile time.
ViewAdapter.java
// some code blocks
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
final Model object = dataset.get(position);
if (Build.VERSION.SDK_INT >= 24) {
// ( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle , Html.FROM_HTML_MODE_LEGACY));
((ImageTypeViewHolder) holder).title.setText(Html.fromHtml(object.title , Html.FROM_HTML_MODE_LEGACY));
((ImageTypeViewHolder) holder).date.setText(Html.fromHtml(object.date , Html.FROM_HTML_MODE_LEGACY));
} else {
// ((ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle));
((ImageTypeViewHolder) holder).title.setText(Html.fromHtml(object.title));
((ImageTypeViewHolder) holder).date.setText(Html.fromHtml(object.date));
}
((ImageTypeViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, WPPostDetails.class);
intent.putExtra("itemPosition", position);
mContext.startActivity(intent);
}
});
JsonApiGlideUrl url = new JsonApiGlideUrl(object.Image);
GlideApp.with(imageView.getContext())
.load(url)
.into(imageView);
}
// some code blocks
OkHttpAppGlideModule.java
import android.content.Context;
import android.support.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
import java.io.InputStream;
/**
* Registers OkHttp related classes via Glide's annotation processor.
*
* <p>For Applications that depend on this library and include an
* {#link AppGlideModule} and Glide's annotation processor, this class
* will be automatically included.
*/
#GlideModule
public final class OkHttpAppGlideModule extends AppGlideModule {
#Override
public void registerComponents(#NonNull Context context, #NonNull Glide glide, #NonNull Registry registry) {
registry.replace(JsonApiGlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
#Override
public boolean isManifestParsingEnabled() {
return false;
}
}
JsonApiGlideUrl.java
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.Headers;
import java.net.URL;
public class JsonApiGlideUrl extends GlideUrl {
public JsonApiGlideUrl(URL url) {
super(url);
}
public JsonApiGlideUrl(String url) {
super(url);
}
public JsonApiGlideUrl(URL url, Headers headers) {
super(url, headers);
}
public JsonApiGlideUrl(String url, Headers headers) {
super(url, headers);
}
}
OkHttpUrlLoader.java
import android.support.annotation.NonNull;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import java.io.InputStream;
import okhttp3.Call;
import okhttp3.OkHttpClient;
/**
* A simple model loader for fetching media over http/https using OkHttp.
*/
public class OkHttpUrlLoader implements ModelLoader<JsonApiGlideUrl, InputStream> {
private final Call.Factory client;
// Public API.
#SuppressWarnings("WeakerAccess")
public OkHttpUrlLoader(#NonNull Call.Factory client) {
this.client = client;
}
#Override
public boolean handles(#NonNull JsonApiGlideUrl url) {
return true;
}
#Override
public LoadData<InputStream> buildLoadData(#NonNull JsonApiGlideUrl model, int width, int height, #NonNull Options options) {
return new LoadData<>(model, new OkHttpStreamFetcher(client, model));
}
/**
* The default factory for {#link OkHttpUrlLoader}s.
*/
// Public API.
#SuppressWarnings("WeakerAccess")
public static class Factory implements ModelLoaderFactory<JsonApiGlideUrl, InputStream> {
private static volatile Call.Factory internalClient;
private final Call.Factory client;
private static Call.Factory getInternalClient() {
if (internalClient == null) {
synchronized (Factory.class) {
if (internalClient == null) {
internalClient = new OkHttpClient();
}
}
}
return internalClient;
}
/**
* Constructor for a new Factory that runs requests using a static singleton client.
*/
public Factory() {
this(getInternalClient());
}
/**
* Constructor for a new Factory that runs requests using given client.
*
* #param client this is typically an instance of {#code OkHttpClient}.
*/
public Factory(#NonNull Call.Factory client) {
this.client = client;
}
#NonNull
#Override
public ModelLoader<JsonApiGlideUrl, InputStream> build(#NonNull MultiModelLoaderFactory multiFactory) {
return new OkHttpUrlLoader(client);
}
#Override
public void teardown() {
// Do nothing, this instance doesn't own the client.
}
}
}
OkHttpStreamFetcher.java
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.HttpException;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import com.bumptech.glide.util.Preconditions;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* Fetches an {#link InputStream} using the okhttp library.
*/
public class OkHttpStreamFetcher implements DataFetcher<InputStream>, okhttp3.Callback {
private static final String TAG = "OkHttpFetcher";
private final Call.Factory client;
private final JsonApiGlideUrl url;
private OkHttpJsonApiFetcher okHttpJsonApiFetcher;
private InputStream stream;
private ResponseBody responseBody;
private DataCallback<? super InputStream> callback;
// call may be accessed on the main thread while the object is in use on other threads. All other
// accesses to variables may occur on different threads, but only one at a time.
private volatile Call call;
// Public API.
#SuppressWarnings("WeakerAccess")
public OkHttpStreamFetcher(Call.Factory client, JsonApiGlideUrl url) {
this.client = client;
this.url = url;
}
#Override
public void loadData(#NonNull Priority priority, #NonNull final DataCallback<? super InputStream> callback) {
okHttpJsonApiFetcher = new OkHttpJsonApiFetcher(client, url);
okHttpJsonApiFetcher.loadData(new DataCallback<GlideUrl>() {
#Override
public void onDataReady(#Nullable GlideUrl data) {
Request.Builder requestBuilder = new Request.Builder().url(data.toStringUrl());
for (Map.Entry<String, String> headerEntry : data.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
OkHttpStreamFetcher.this.callback = callback;
call = client.newCall(request);
call.enqueue(OkHttpStreamFetcher.this);
}
#Override
public void onLoadFailed(#NonNull Exception e) {
callback.onLoadFailed(e);
}
});
}
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "OkHttp failed to obtain result", e);
}
callback.onLoadFailed(e);
}
#Override
public void onResponse(#NonNull Call call, #NonNull Response response) {
responseBody = response.body();
if (response.isSuccessful()) {
long contentLength = Preconditions.checkNotNull(responseBody).contentLength();
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
callback.onDataReady(stream);
} else {
callback.onLoadFailed(new HttpException(response.message(), response.code()));
}
}
#Override
public void cleanup() {
okHttpJsonApiFetcher.cleanup();
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
// Ignored
}
if (responseBody != null) {
responseBody.close();
}
callback = null;
}
#Override
public void cancel() {
okHttpJsonApiFetcher.cancel();
Call local = call;
if (local != null) {
local.cancel();
}
}
#NonNull
#Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
#NonNull
#Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
}
OkHttpJsonApiFetcher.java
import android.support.annotation.NonNull;
import android.util.Log;
import com.bumptech.glide.load.HttpException;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* Fetches an {#link InputStream} using the okhttp library.
*/
public class OkHttpJsonApiFetcher implements okhttp3.Callback {
private static final String TAG = "OkHttpJsonApiFetcher";
private final Call.Factory client;
private final JsonApiGlideUrl url;
private ResponseBody responseBody;
private DataFetcher.DataCallback<? super GlideUrl> callback;
// call may be accessed on the main thread while the object is in use on other threads. All other
// accesses to variables may occur on different threads, but only one at a time.
private volatile Call call;
// Public API.
#SuppressWarnings("WeakerAccess")
public OkHttpJsonApiFetcher(Call.Factory client, JsonApiGlideUrl url) {
this.client = client;
this.url = url;
}
public void loadData(#NonNull final DataFetcher.DataCallback<? super GlideUrl> callback) {
Request.Builder requestBuilder = new Request.Builder().get().url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
this.callback = callback;
call = client.newCall(request);
call.enqueue(this);
}
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "OkHttp failed to obtain result", e);
}
callback.onLoadFailed(e);
}
#Override
public void onResponse(#NonNull Call call, #NonNull Response response) {
responseBody = response.body();
if (response.isSuccessful() && responseBody != null) {
try {
String json = responseBody.string();
String url = JsonApiDataModel.getSourceUrl(json);
callback.onDataReady(new GlideUrl(url));
} catch (IOException e) {
callback.onLoadFailed(new HttpException(response.message(), response.code()));
e.printStackTrace();
}
} else {
callback.onLoadFailed(new HttpException(response.message(), response.code()));
}
}
public void cleanup() {
if (responseBody != null) {
responseBody.close();
}
callback = null;
}
public void cancel() {
Call local = call;
if (local != null) {
local.cancel();
}
}
}
JsonApiDataModel.java
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
public class JsonApiDataModel {
#SerializedName("media_details")
MediaDetails mediaDetails;
public static String getSourceUrl(String json) {
return new Gson().fromJson(json, JsonApiDataModel.class).mediaDetails.sizes.full.sourceUrl;
}
public class MediaDetails {
#SerializedName("sizes")
Sizes sizes;
}
public class Sizes {
// you can use full, medium or thumbnail here!
#SerializedName("full")
Full full;
}
public class Full {
#SerializedName("source_url")
String sourceUrl;
}
}
.
Test Code & Visual Result:
import com.aminography.glideapplication.glide.okhttp3.GlideApp;
import com.aminography.glideapplication.glide.okhttp3.JsonApiGlideUrl;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView imageView = findViewById(R.id.imageView);
TextView textView = findViewById(R.id.textView);
String sourceUrl = "https://www.myfitbytes.com/wp-json/wp/v2/media/2811";
textView.setText("JsonApiGlideUrl:\n\n" + sourceUrl);
final JsonApiGlideUrl url = new JsonApiGlideUrl(sourceUrl);
GlideApp.with(MainActivity.this).load(url).into(imageView);
}
}
• First of all, I strongly recommend you to use Glide v4.
The loading delay problem and image item disposition comes from onBindViewHolder method where you call a webservice in it. Everytime a list item is appeared in scrolling, onBindViewHolder is called to initialize the view with correct values. So you should not call a webservice here at all (but in the code it is called everytime). As Glide creates a queue of requests, it guarantees that the image would be downloaded only one time because it caches them. Also it guarantees that the image would be loaded if the ImageView is in visible area of the RecyclerView in scrolling process. In addition, by setting diskCacheStrategy to DiskCacheStrategy.ALL, Glide shows an image which is resized according to the ImageView size.
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
final Model object = dataset.get(position);
if (Build.VERSION.SDK_INT >= 24) {
// ( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle , Html.FROM_HTML_MODE_LEGACY));
((ImageTypeViewHolder) holder).title.setText(Html.fromHtml(object.title , Html.FROM_HTML_MODE_LEGACY));
((ImageTypeViewHolder) holder).date.setText(Html.fromHtml(object.date , Html.FROM_HTML_MODE_LEGACY));
} else {
// ((ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle));
((ImageTypeViewHolder) holder).title.setText( Html.fromHtml(object.title));
((ImageTypeViewHolder) holder).date.setText( Html.fromHtml(object.date));
}
((ImageTypeViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, WPPostDetails.class);
intent.putExtra("itemPosition", position);
mContext.startActivity(intent);
}
});
Glide.with(imageView.getContext())
.load(object.Image)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
.into(imageView);
}
By the above changes, your RecyclerView should work smoothly as well as fast. If you want to make Glide more integrated with RecyclerView, you can follow this. But I think this is not necessary and the previous level is enough.
The current version for Glide is '4.8.0'. Any reason you don't want to work with that version? https://github.com/bumptech/glide
https://github.com/bumptech/glide/blob/master/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoGrid.java
You need to use a RecyclerView. Here is an example:
grid.addItemDecoration(new RecyclerView.ItemDecoration() {
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
outRect.set(gridMargin, gridMargin, gridMargin, gridMargin);
}
});
grid.setRecyclerListener(new RecyclerView.RecyclerListener() {
#Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
PhotoViewHolder photoViewHolder = (PhotoViewHolder) holder;
GlideApp.with(FlickrPhotoGrid.this).clear(photoViewHolder.imageView);
}
});
I need help with Dagger2.13 for Android.
I am following several examples on the internet but I am now facing an error that I can not solve.
Error:(23, 14) error: #Subcomponent.Builder is missing setters for
required modules or subcomponents:
[com.hugothomaz.fipe.Module.DIMarcaModulo]
I thought it best to post the problem classes in GITHub and include the repository link here.
https://github.com/hugothomaz/FIPE_Test_Dagger2.11
-FipeApplication-
public class FipeApplication extends Application implements HasActivityInjector, HasFragmentInjector{
private static final String URL_SEARCH = "http://fipeapi.appspot.com/api/1/";
#Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjectorFragment;
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjectorActivity;
#Override
public void onCreate() {
super.onCreate();
initializeApplicationComponente();
}
#Override
public void onTerminate() {
super.onTerminate();
}
private void initializeApplicationComponente() {
Log.i("app", "FipeApplication initializeApplicationComponente");
//DaggerDIApplicationComponent.builder().(this).build();
}
#Override
public AndroidInjector<Fragment> fragmentInjector() {
return dispatchingAndroidInjectorFragment;
}
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjectorActivity;
}
}
-DIApplicationModulo-
#Module(subcomponents = {DIMarcaComponent.class})
public class DIApplicationModulo {
#Provides
#Singleton
GsonConverterFactory provideGsonConverterFactory(){
GsonConverterFactory factory = GsonConverterFactory.create();
return factory;
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(){
return new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
}
#Provides
#Singleton
RxJavaCallAdapterFactory provideRxJavaCallAdapterFactory(){
return RxJavaCallAdapterFactory.create();
}
#Provides
#Singleton
Retrofit provideRetrofit(OkHttpClient client,
GsonConverterFactory converterFactory,
RxJavaCallAdapterFactory adapterFactory, String mBaseURL){
return new Retrofit.Builder()
.baseUrl(mBaseURL)
.addConverterFactory(converterFactory)
.addCallAdapterFactory(adapterFactory)
.client(client)
.build();
}
}
-DIApplicationComponent-
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
DIApplicationModulo.class,
ViewBuilderModule.class
})
public interface DIApplicationComponent extends AndroidInjector<FipeApplication>{
#Component.Builder
interface Builder{
#BindsInstance
DIApplicationComponent.Builder baseURL(String mBaseURL);
DIApplicationComponent build();
}
}
-ViewBuilderModule-
#Module(subcomponents = {DIMarcaComponent.class})
public abstract class ViewBuilderModule {
#Binds
#IntoMap
#FragmentKey(MarcaFragment.class)
abstract AndroidInjector.Factory<? extends Fragment> bindMarcaFragment(DIMarcaComponent.Builder bulder);
}
-DIMarcaModulo-
#Module
public class DIMarcaModulo {
private MarcaFragment mView;
private MarcaAdapterRecyclerImpl mAdapter;
public Context mContext;
public DIMarcaModulo(MarcaFragment view, MarcaAdapterRecyclerImpl adapter){
this.mView = view;
this.mAdapter = adapter;
this.mContext = view.getActivity().getBaseContext();
Log.i("app", "DIMarcaModulo instanciado");
if(adapter==null){
Log.i("app", "DIMarcaModulo - adapter : nao instanciado");
}else{
Log.i("app", "DIMarcaModulo - adapter : instancied");
}
}
#Provides
#PerFragment
IMarcaAPI provideMarcaApi(Retrofit retrofit){
Log.i("app", "DIMarcaModulo provideMarcaApi");
return retrofit.create(IMarcaAPI.class);
}
#Provides
#PerFragment
BaseView provideMarcaFragment(){
Log.i("app", "DIMarcaModulo provideMarcaFragment");
return mView;
}
#Provides
#PerFragment
IMarcaAdapterModel provideMarcaAdapterModel(){
Log.i("app", "DIMarcaModulo provideMarcaAdapterModel");
return mAdapter;
}
#Provides
#PerFragment
IMarcaPresenter provideMarcaPresenter(){
Log.i("app", "DIMarcaModulo provideMarcaPresenter");
return new MarcaPresenterImpl();
}
#Provides
#PerFragment
ControllerServiceAPIRest provideControllerServiceAPIRest(){
Log.i("app", "DIMarcaModulo ControllerServiceAPIRest");
return new ControllerServiceAPIRest();
}
#Provides
#PerFragment
Context exposeContext() {
return mContext;
}
}
-DIMarcaComponent-
#PerFragment
#Subcomponent(modules = {DIMarcaModulo.class})
public interface DIMarcaComponent extends AndroidInjector<MarcaFragment> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaFragment>{
}
}
-MarcaFragment-
public class MarcaFragment extends BaseFragment implements IMarcaView, HasFragmentInjector{
private MarcaAdapterRecyclerImpl mMarcaAdapter;
private LinearLayoutManager llm;
private View view = null;
#Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
#Inject
public IMarcaPresenter mMarcaPresenter;
#BindView(R.id.rv_marca)
protected RecyclerView mRecyclerMarca;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if(view==null){
view = inflater.inflate(R.layout.fragment_marca, container, false);
}
setUnBinder(ButterKnife.bind(this, view));
return view;
}
#Override
protected void onViewReady(Bundle saveInstanceState, Intent intent) {
initializeRecyclerMarca();
super.onViewReady(saveInstanceState, intent);
if(mMarcaPresenter != null){
Log.i("app", "MarcaFragment - Presenter nao esta vazio");
}else{
Log.i("app", "MarcaFragment - Presenter esta vazio");
}
mMarcaPresenter.initSerice();
}
private void initializeRecyclerMarca() {
if(mMarcaAdapter==null){
mMarcaAdapter = new MarcaAdapterRecyclerImpl();
}
mRecyclerMarca.setHasFixedSize(true);
llm = new LinearLayoutManager(getActivity().getBaseContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerMarca.setLayoutManager(llm);
mRecyclerMarca.setAdapter(mMarcaAdapter);
}
#Override
public void onOpenVehicleFragmentByMarcaClicked(Marca marca) {
// recebendo o item clicado para chamar proxima tela com a Marca clicada.
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
AndroidInjection.inject(this);
}
#Override
public AndroidInjector<Fragment> fragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
}
-MarcaPresenterImpl-
public class MarcaPresenterImpl extends BasePresenter<IMarcaView> implements IMarcaPresenter{
private static final String TAG_MARCA_PRESENTER = "MarcaPresenterImpl";
#Inject
public IMarcaAdapterModel mMarcaAdapterModel;
#Inject
public ControllerServiceAPIRest mControllerServiceAPIRest;
#Inject
public MarcaPresenterImpl(){
}
#Override
public void onShowMessage(String message) {
getView().onShowMessage(message);
}
#Override
public void onHideMessage() {
getView().onHideMessage();
}
#Override
public void onShowProgress(String message) {
getView().onShowProgress(message);
}
#Override
public void onHideProgress() {
getView().onHideProgress();
}
#Override
public void onShowToast(String message) {
getView().onShowToast(message);
}
public void refresh() {
mMarcaAdapterModel.refresh();
}
#Override
public void refreshItem(int id) {
}
#Override
public void listMarcaByServiceForView(List<Marca> listMarca) {
mMarcaAdapterModel.setListMarca(listMarca);
}
#Override
public void initSerice() {
mControllerServiceAPIRest.getMarca();
}
#Override
public void getMarcaClicked(#NonNull Marca marca) {
getView().onOpenVehicleFragmentByMarcaClicked(marca);
}
}
-ControllerServiceAPIRest-
public class ControllerServiceAPIRest implements Observer<List<Marca>> {
#Inject
public IMarcaPresenter mPresenter;
#Inject
public IMarcaAPI mMarcaAPI;
private List<Marca> listMarca;
#Inject
public ControllerServiceAPIRest() {
}
protected void getMarca(){
if(listMarca == null){
listMarca = new ArrayList<>();
}
mPresenter.onShowProgress("Carregando Marca de Veículos...");
Observable<List<Marca>> cakesResponseObservable = mMarcaAPI.getAllMarca();
subscribe(cakesResponseObservable, this);
}
private void subscribe(Observable<List<Marca>> observable, Observer<List<Marca>> observer){
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
#Override
public void onError(Throwable e) {
mPresenter.onHideMessage();
mPresenter.onHideProgress();
mPresenter.onShowToast("Erro ao carregar Marcas!");
Log.e("app", "Falha no carregamento de Marcas: " + e.getMessage());
new Exception(e.getMessage());
}
#Override
public void onComplete() {
mPresenter.onHideMessage();
mPresenter.onHideProgress();
Log.i("app", "ControllerServiceAPIRest - listMarca Position 0: " + listMarca.get(0));
if (listMarca==null && !listMarca.isEmpty() && listMarca.get(0)!=null){
mPresenter.listMarcaByServiceForView(listMarca);
mPresenter.onShowToast("Marcas carregadas!");
}else{
mPresenter.onShowToast("Lista não foi carregada!");
}
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Marca> list) {
listMarca = list;
}
}
You require your Module:
#PerFragment
#Subcomponent(modules = {DIMarcaModulo.class})
public interface DIMarcaComponent extends AndroidInjector<MarcaFragment> {
And Dagger can't create it, because it doesn't have a public parameterless constructor:
#Module
public class DIMarcaModulo {
// ...
public DIMarcaModulo(MarcaFragment view, MarcaAdapterRecyclerImpl adapter){
But you bind in your Builder directly:
#Module(subcomponents = {DIMarcaComponent.class})
public abstract class ViewBuilderModule {
#Binds
#IntoMap
#FragmentKey(MarcaFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindMarcaFragment(DIMarcaComponent.Builder bulder);
}
So when dagger.android tries to create your object:
// AndroidInjector.Builder
abstract class Builder<T> implements AndroidInjector.Factory<T> {
#Override
public final AndroidInjector<T> create(T instance) {
seedInstance(instance);
return build();
}
It notices that you haven't provided a DIMarcaModulo instance and fails. You'll need to follow the advice in the SO question Dagger 2.10 Android subcomponents and builders, which means either giving DIMarcaModulo a public parameterless constructor that can inject MarcaFragment, or by overriding DIMarcaComponent.Builder#seedInstance:
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaFragment> {
// This method tells Dagger you need to supply your own DIMarcoModulo.
public abstract void diMarcoModulo(DIMarcaModulo modulo);
// dagger.android calls this method automatically, but only this method, so
// you'll need to call diMarcoModulo from it.
#Override public void seedInstance(MarcaFragment fragment) {
diMarcoModulo(fragment, fragment.getMMarcaAdapter());
bindMarcaFragment(fragment); // OPTIONAL: See below
}
// If you want MarcaFragment to remain injectable, you might need to call into
// a different #BindsInstance method you define, because you've prevented
// seedInstance from doing that for you.
#BindsInstance public abstract void bindMarcaFragment(MarcaFragment fragment);
}
As you can see, MarcaFragment will automatically be available in the graph that DIMarcoModulo installs, so if you can avoid the constructor parameters and instead receive the fragment as a parameter to #Provides methods, your code might be easier to read. You also will have trouble with the method I called fragment.getMMarcaAdapter(), because you inject onAttach and you get access to the RecyclerView onCreateView. However, this shouldn't be a big problem if you remove the constructor parameters and if you can make sure you don't try to access the RecyclerView until after Android has a chance to create it.
Thanks Jeff Bowman, it was my dependencies that were wrong.
Now it works like this:
annotationProcessor "com.google.dagger:dagger-compiler:2.13"
annotationProcessor "com.google.dagger:dagger-android-processor:2.13"
compile "com.google.dagger:dagger:2.13"
compile "com.google.dagger:dagger-android:2.13"
compile 'com.google.dagger:dagger-android-support:2.13'
I am trying to merge 2 android studio projects into one.
Here's my app level build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.cricketbuzz"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
debuggable true
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support:cardview-v7:23.4.0'
compile 'com.android.support:recyclerview-v7:23.4.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.github.JakeWharton:ViewPagerIndicator:2.4.1#aar'
compile 'com.android.support:multidex:1.0.0'
compile 'com.cricketbuzzsdk:CricketBuzzSDK:1.0.0'
compile 'com.wang.avi:library:2.1.3'
compile project(':basketball')
}
Here's my library build.gradle
apply plugin: 'com.android.library'
android {
signingConfigs {
config {
}
}
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
// applicationId "com.gmail.jorgegilcavazos.ballislife"
minSdkVersion 19
targetSdkVersion 23
versionCode 3
versionName "0.1.0"
signingConfig signingConfigs.config
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/LICENCE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE.txt'
}
productFlavors {
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:3.1.2'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.mcxiaoke.volley:library:1.0.19'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.google.android.gms:play-services-gcm:8.3.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support:support-v4:23.4.0'
compile 'com.android.support:recyclerview-v7:23.4.0'
compile 'com.android.support:cardview-v7:23.4.0'
compile 'com.android.support:customtabs:23.3.0'
compile 'net.dean.jraw:JRAW:0.9.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.jakewharton:butterknife:8.4.0'
}
The error I am getting is :
C:\Users\karanc\AndroidStudioProjects\CricketBuzz\basketball\src\main\java\com\gmail\jorgegilcavazos\ballislife\features\games\GameAdapter.java
Error:(112, 23) error: element value must be a constant expression
C:\Users\karanc\AndroidStudioProjects\CricketBuzz\basketball\src\main\java\com\gmail\jorgegilcavazos\ballislife\features\gamethread\CommentThreadFragment.java
Error:(127, 22) error: constant expression required
Here are GameAdapter & CommentThread files:
GameAdapter.java
package com.gmail.jorgegilcavazos.ballislife.features.games;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.gmail.jorgegilcavazos.ballislife.features.data.NbaGame;
import com.gmail.jorgegilcavazos.ballislife.R;
import com.gmail.jorgegilcavazos.ballislife.util.Utilities;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import static android.support.design.R.styleable.RecyclerView;
/**
* RecyclerView Adapter used by the {#link GamesFragment} to display a list of games.
*/
public class GameAdapter extends RecyclerView.Adapter<GameAdapter.GameViewHolder> {
private Context context;
private List<NbaGame> nbaGameList;
private GamesFragment.GameItemListener gameItemListener;
public GameAdapter(List<NbaGame> nbaGames,
GamesFragment.GameItemListener itemListener) {
nbaGameList = nbaGames;
gameItemListener = itemListener;
}
#Override
public GameViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_game,
parent, false);
context = parent.getContext();
return new GameViewHolder(view);
}
#Override
public void onBindViewHolder(final GameViewHolder holder, int position) {
NbaGame nbaGame = nbaGameList.get(position);
int resKeyHome = context.getResources().getIdentifier(nbaGame.getHomeTeamAbbr()
.toLowerCase(), "drawable", context.getPackageName());
int resKeyAway = context.getResources().getIdentifier(nbaGame.getAwayTeamAbbr()
.toLowerCase(), "drawable", context.getPackageName());
holder.tvHomeTeam.setText(nbaGame.getHomeTeamAbbr());
holder.tvAwayTeam.setText(nbaGame.getAwayTeamAbbr());
holder.ivHomeLogo.setImageResource(resKeyHome);
holder.ivAwayLogo.setImageResource(resKeyAway);
holder.tvHomeScore.setText(nbaGame.getHomeTeamScore());
holder.tvAwayScore.setText(nbaGame.getAwayTeamScore());
holder.tvClock.setText(nbaGame.getGameClock());
holder.tvPeriod.setText(Utilities.getPeriodString(nbaGame.getPeriodValue(),
nbaGame.getPeriodName()));
holder.tvHomeScore.setVisibility(View.GONE);
holder.tvAwayScore.setVisibility(View.GONE);
holder.tvClock.setVisibility(View.GONE);
holder.tvPeriod.setVisibility(View.GONE);
holder.tvFinal.setVisibility(View.GONE);
holder.tvTime.setVisibility(View.GONE);
switch (nbaGame.getGameStatus()) {
case NbaGame.PRE_GAME:
holder.tvTime.setVisibility(View.VISIBLE);
holder.tvTime.setText(nbaGame.getPeriodStatus());
break;
case NbaGame.IN_GAME:
holder.tvHomeScore.setVisibility(View.VISIBLE);
holder.tvAwayScore.setVisibility(View.VISIBLE);
holder.tvClock.setVisibility(View.VISIBLE);
holder.tvPeriod.setVisibility(View.VISIBLE);
break;
case NbaGame.POST_GAME:
holder.tvHomeScore.setVisibility(View.VISIBLE);
holder.tvAwayScore.setVisibility(View.VISIBLE);
holder.tvFinal.setVisibility(View.VISIBLE);
holder.tvFinal.setText("FINAL");
break;
}
holder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
gameItemListener.onGameClick(nbaGameList.get(holder.getAdapterPosition()));
}
});
}
#Override
public int getItemCount() {
return null != nbaGameList ? nbaGameList.size() : 0;
}
public void swap(List<NbaGame> data) {
nbaGameList.clear();
nbaGameList.addAll(data);
notifyDataSetChanged();
}
public static class GameViewHolder extends RecyclerView.ViewHolder {
View container;
#BindView(R.id.homelabel) TextView tvHomeTeam;
#BindView(R.id.awaylabel) TextView tvAwayTeam;
#BindView(R.id.homescore) TextView tvHomeScore;
#BindView(R.id.awayscore) TextView tvAwayScore;
#BindView(R.id.clock) TextView tvClock;
#BindView(R.id.period) TextView tvPeriod;
#BindView(R.id.extraLabel) TextView tvTime;
#BindView(R.id.extraLabel2) TextView tvFinal;
#BindView(R.id.homeicon) ImageView ivHomeLogo;
#BindView(R.id.awayicon) ImageView ivAwayLogo;
public GameViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
container = view;
}
}
}
CommentThread.java
package com.gmail.jorgegilcavazos.ballislife.features.gamethread;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import com.gmail.jorgegilcavazos.ballislife.features.shared.CommentAdapter;
import com.gmail.jorgegilcavazos.ballislife.R;
import com.gmail.jorgegilcavazos.ballislife.util.AuthListener;
import com.gmail.jorgegilcavazos.ballislife.util.MyDebug;
import com.gmail.jorgegilcavazos.ballislife.network.RedditAuthentication;
import com.gmail.jorgegilcavazos.ballislife.util.RedditUtils;
import net.dean.jraw.http.SubmissionRequest;
import net.dean.jraw.models.CommentNode;
import net.dean.jraw.models.CommentSort;
import net.dean.jraw.models.Listing;
import net.dean.jraw.models.Submission;
import net.dean.jraw.paginators.Sorting;
import net.dean.jraw.paginators.SubredditPaginator;
import java.util.ArrayList;
import java.util.List;
public class CommentThreadFragment extends Fragment {
private static final String TAG = "CommentThreadFragment";
public interface SubmissionListingFetchListener {
void onSuccess(Listing<Submission> submissions);
void onFailure(String message);
}
public interface FullSubmissionFetchListener {
void onSuccess(Submission submission);
void onFailure(String message);
}
public static final String HOME_TEAM_KEY = "HOME_TEAM";
public static final String AWAY_TEAM_KEY = "AWAY_TEAM";
public static final String THREAD_TYPE_KEY = "THREAD_TYPE";
private static final String NBA_SUBREDDIT = "nba";
private static final int SEARCH_LIMIT = 100;
private static final int RETRY_FIND_SUBMISSION = 0;
private static final int RETRY_FETCH_COMMENTS = 1;
private String mHomeTeam;
private String mAwayTeam;
private String mThreadId;
private boolean mFoundThreadId;
private RedditUtils.GameThreadType mThreadType;
Context mContext;
View rootView;
RecyclerView mRecyclerView;
RecyclerView.LayoutManager mLayoutManager;
CommentAdapter mCommentAdapter;
ProgressBar mProgressBar;
Snackbar mSnackbar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mHomeTeam = getArguments().getString(HOME_TEAM_KEY);
mAwayTeam = getArguments().getString(AWAY_TEAM_KEY);
mThreadType = (RedditUtils.GameThreadType) getArguments().get(THREAD_TYPE_KEY);
}
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_comment_thread, container, false);
mProgressBar = (ProgressBar) rootView.findViewById(R.id.progressBar);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.comment_thread_rv);
mLayoutManager = new LinearLayoutManager(mContext);
mRecyclerView.setLayoutManager(mLayoutManager);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mRecyclerView.setNestedScrollingEnabled(false);
} else {
ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
}
return rootView;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContext = getActivity();
if (mFoundThreadId) {
fetchComments();
} else {
findGameSubmission();
}
}
#Override
public void onPause() {
dismissSnackbar();
super.onPause();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh:
dismissSnackbar();
if (mFoundThreadId) {
fetchComments();
} else {
findGameSubmission();
}
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Starts a {#link FetchSubmissionListing} task that retrieves a list of submissions in the
* /r/nba subreddit. If it is successfully retrieved, it tries to find the one that belongs
* to this game and shows its comment tree.
*/
private void findGameSubmission() {
AuthListener authListener = new AuthListener() {
#Override
public void onSuccess() {
findGameSubmission();
}
#Override
public void onFailure() {
showSnackBar("Failed to connect to Reddit", true, RETRY_FIND_SUBMISSION);
}
};
SubmissionListingFetchListener fetchListener = new SubmissionListingFetchListener() {
#Override
public void onSuccess(Listing<Submission> submissions) {
mThreadId = RedditUtils.findNbaGameThreadId(submissions, mThreadType,
mHomeTeam, mAwayTeam);
if (mThreadId != null) {
mFoundThreadId = true;
fetchComments();
} else {
mFoundThreadId = false;
showSnackBar("No comment thread found", true, RETRY_FIND_SUBMISSION);
}
}
#Override
public void onFailure(String message) {
showSnackBar(message, true, RETRY_FIND_SUBMISSION);
}
};
new FetchSubmissionListing(mContext, NBA_SUBREDDIT, SEARCH_LIMIT, Sorting.NEW,
fetchListener, authListener).execute();
}
/**
* Starts a {#link FetchFullSubmission} task that retrieves the Submission of the given
* submissionId. A "full" submissions is one that also contains its comment tree.
*/
private void fetchComments() {
AuthListener authListener = new AuthListener() {
#Override
public void onSuccess() {
fetchComments();
}
#Override
public void onFailure() {
showSnackBar("Failed to connect to Reddit", true, RETRY_FETCH_COMMENTS);
}
};
FullSubmissionFetchListener fetchListener = new FullSubmissionFetchListener() {
#Override
public void onSuccess(Submission submission) {
loadComments(submission);
}
#Override
public void onFailure(String message) {
showSnackBar(message, true, RETRY_FIND_SUBMISSION);
}
};
new FetchFullSubmission(mContext, mThreadId, fetchListener, authListener).execute();
}
/**
* Loads a tree of comments into the RecyclerView, given a Reddit Submission.
*/
private void loadComments(Submission submission) {
Iterable<CommentNode> iterable = submission.getComments().walkTree();
List<CommentNode> commentNodes = new ArrayList<>();
for (CommentNode node : iterable) {
commentNodes.add(node);
}
mCommentAdapter = new CommentAdapter(mContext, commentNodes);
mRecyclerView.setAdapter(mCommentAdapter);
}
private void showSnackBar(String message, boolean retry, final int retryCode) {
mSnackbar = Snackbar.make(rootView, message,
Snackbar.LENGTH_INDEFINITE);
if (retry) {
mSnackbar.setAction("RETRY", new View.OnClickListener() {
#Override
public void onClick(View v) {
switch (retryCode) {
case RETRY_FIND_SUBMISSION:
findGameSubmission();
break;
case RETRY_FETCH_COMMENTS:
fetchComments();
break;
}
}
});
}
mSnackbar.show();
mProgressBar.setVisibility(View.GONE);
}
private void dismissSnackbar() {
if (mSnackbar != null && mSnackbar.isShown()) {
mSnackbar.dismiss();
}
}
/**
* Retrieves a Listing of Reddit Submissions, given a subreddit, a limit of submissions and a
* sorting option.
*/
private class FetchSubmissionListing extends AsyncTask<Void, Void, Listing<Submission>> {
private Context mContext;
private String mSubreddit;
private int mLimit;
private Sorting mSorting;
private SubmissionListingFetchListener mFetchListener;
private AuthListener mAuthListener;
public FetchSubmissionListing(Context context, String subreddit, int limit, Sorting sorting,
SubmissionListingFetchListener fetchListener,
AuthListener authListener) {
mContext = context;
mSubreddit = subreddit;
mLimit = limit;
mSorting = sorting;
mFetchListener = fetchListener;
mAuthListener = authListener;
}
#Override
protected void onPreExecute() {
mProgressBar.setVisibility(View.VISIBLE);
mRecyclerView.setVisibility(View.GONE);
}
#Override
protected Listing<Submission> doInBackground(Void... params) {
RedditAuthentication redditAuthentication = RedditAuthentication.getInstance();
if (redditAuthentication.getRedditClient().isAuthenticated()) {
SubredditPaginator paginator = new SubredditPaginator(
redditAuthentication.getRedditClient(), mSubreddit);
paginator.setLimit(mLimit);
paginator.setSorting(mSorting);
try {
return paginator.next(false /* forceNetwork */);
} catch (Exception e) {
if (MyDebug.LOG) {
Log.d(TAG, "Reddit auth error on FetchSubmissionListing.");
}
}
} else {
mFetchListener.onFailure("Failed to connect to Reddit");
}
return null;
}
#Override
protected void onPostExecute(Listing<Submission> submissions) {
if (submissions != null) {
mFetchListener.onSuccess(submissions);
} else {
if (!RedditAuthentication.getInstance().getRedditClient().isAuthenticated()) {
// Attempt to authenticate once.
RedditAuthentication.getInstance().authenticate(mContext, mAuthListener);
}
mFetchListener.onFailure("Failed to connect to Reddit");
}
}
}
/**
* Retrieves a "full" Reddit submission given a Reddit submisisonId. A "full" submission is one
* that also contains its comment tree.
* The sorting of the thread is determined by mThreadType (Live game or post game).
*/
private class FetchFullSubmission extends AsyncTask<Void, Void, Submission> {
private Context mContext;
private String mThreadId;
private FullSubmissionFetchListener mFetchListener;
private AuthListener mAuthListener;
public FetchFullSubmission(Context context, String threadId,
FullSubmissionFetchListener fetchListener,
AuthListener authListener) {
mContext = context;
mThreadId = threadId;
mFetchListener = fetchListener;
mAuthListener = authListener;
}
#Override
protected void onPreExecute() {
mProgressBar.setVisibility(View.VISIBLE);
mRecyclerView.setVisibility(View.GONE);
}
#Override
protected Submission doInBackground(Void... params) {
if (mThreadId == null) {
return null;
}
SubmissionRequest.Builder builder = new SubmissionRequest.Builder(mThreadId);
switch (mThreadType) {
case LIVE_GAME_THREAD:
builder.sort(CommentSort.NEW);
break;
case POST_GAME_THREAD:
builder.sort(CommentSort.TOP);
break;
default:
builder.sort(CommentSort.TOP);
break;
}
SubmissionRequest submissionRequest = builder.build();
try {
return RedditAuthentication.getInstance()
.getRedditClient().getSubmission(submissionRequest);
} catch (Exception e) {
if (MyDebug.LOG) {
Log.d(TAG, "Could not load submission in FetchFullSubmission.");
}
}
return null;
}
#Override
protected void onPostExecute(Submission submission) {
mProgressBar.setVisibility(View.GONE);
if (submission != null) {
mFetchListener.onSuccess(submission);
mRecyclerView.setVisibility(View.VISIBLE);
} else {
if (!RedditAuthentication.getInstance().getRedditClient().isAuthenticated()) {
// Attempt to re-authenticate once.
RedditAuthentication.getInstance().authenticate(mContext, mAuthListener);
}
mFetchListener.onFailure("Failed to connect to Reddit");
}
}
}
}
How to get rid of this??
Screenshots of errors
:
You have implemented Butterknife in your library project. Butterknife doesnot work for library project.For more details you can follow this link
https://github.com/JakeWharton/butterknife/issues/45
As pointed out on this github issue, it can work by using R2.id instead of R.id when we are using Butterknife in library project.
I've implemented the Google Play Games API in my game recently, but some issues appeared after that.
The problem is for some reason my game is not loading my Custom View class when I start the aplication and call the method setContentView() but this only happens when I build the release version of the apk.
gv = new GameView(this);
setContentView(gv); //not loading on release version or if the debuggable is set to true in build.gradle
I also tryied to put this line on build.gradle
debuggable false
the result was that the release version started working but with this line I cant upload it to playStore and the reason of this thread is obviusly because I need this working on the playStore.
Some Notes of test:
The Google play services connects well both release and debug apks, but in release a black screen appears isntead of my GameView Class
The setContentView() doesn't works when running the apk on release version or if the debuggable is set to true in build.gradle.
The setContentView() works fine when running the apk on debug version
The setContentView() works fine if I load a XML file in any situations (but my game doesnt uses any XMLs).
Main Activity Class
public class Main extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
GameView gv;
public static DBHandler db;
public static String version = "0.0.0";
public static GoogleApiClient mGoogleApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fullScreen(true); //PRE ESTABELECE O TAMANHO DA TELA DEIXANDO NO FULLSCREEN PRIMEIRAMENTE
(Toast.makeText(this, "inicializando GameView", Toast.LENGTH_SHORT)).show();
gv = new GameView(this);
setContentView(gv);
// Create the Google Api Client with access to the Play Games services
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Games.API).addScope(Games.SCOPE_GAMES)
// add other APIs and scopes here as needed
.setViewForPopups(gv)
.build();
// ...
//setContentView(R.layout.loading_screen);
db = new DBHandler(this);
Log.w("Main", ""+db.getPointsCount()+" - registros encontrados.");
//VERSAO ---
PackageInfo pInfo = null;
try {
pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}// ...
}
protected void onDestroy(){
super.onDestroy();
gv.relase();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
fullScreen(hasFocus);
}
void fullScreen(boolean hasFocus){
if (hasFocus) {
final View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}
}
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
Log.e("Main","Conectando ao Google Play...");
}
#Override
protected void onStop() {
super.onStop();
mGoogleApiClient.disconnect();
Log.e("Main","Desconectado da google API PlayGames!");
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Log.e("Main","onConnected");
Games.Achievements.unlock(mGoogleApiClient, "CgkI8au7i4oUEAIQAg");
(Toast.makeText(this, "Conectado ao Google Play Game", Toast.LENGTH_SHORT)).show();
}
#Override
public void onConnectionSuspended(int i) {
Log.e("Main","onConnectionSuspended");
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.e("Main","onConnectionFailed");
(Toast.makeText(this, "Não foi posivel conectar ao Google Play Services.", Toast.LENGTH_SHORT)).show();
}
}
Build.gradle
apply plugin: 'com.android.application'
android {
signingConfigs {
DannarkKey {
keyAlias 'dannark'
keyPassword '23021994'
storeFile file('C:/Users/Daniel/AndroidstudioProjects/DannarkKey.jks')
storePassword '23021994'
}
}
compileSdkVersion 23
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "com.jumpers.gamelogic"
minSdkVersion 14
targetSdkVersion 21
versionCode 24
versionName "2.7.4"
}
buildTypes {
release {
debuggable false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.google.android.gms:play-services:9.0.0'
}
GameView.class
/**
* Created by Daniel on 19/03/2016.
*/
public class GameView extends View implements Runnable {
Context context;
private static final int INTERVAL = 10;
private boolean running = true;
private Paint p;
public static HUD hud;
public static PrintText pt;
public static ArrayList<SpawnMessageWindows> spawnMsgs = new ArrayList<>();
public static ScoreScreen scoreScreen;
public static HandleEventsTouchsSpawnedObjects handleEventsTouchs = new HandleEventsTouchsSpawnedObjects();
public static Load load;
public static Fase fase;
public static Player player;
public static Menu menu;
public static Camera cam;
Bitmap layerImg2;
public static Canvas layer2;
public static LaterCanvas laterCanvas;
public static int lar;
public static int alt;
public static int touchFocus = 0;
public static ArrayList<String> variaveisName = new ArrayList<>();
public static ArrayList<Float> variaveisValue = new ArrayList<>();
private long preTime = 0;
public static int timeInGame = 0;
public static boolean isPlaying = false;
// blocksIDsUsedForTutorial TEM O OBJETIVO DE MOSTRAR MENSSAGENS DE TUTORIAS PELA PRIMEIRA VES CASO VC NUNCA TENHA USADO
// UM SCRIPT, SE TIVER DENTRO DESSA LISTA, ENTÃO A MENSAGEM NAO APARECERA.
public static ArrayList<Integer> blocksIDsUsedForTutorial = new ArrayList<>();
public GameView(Context context) {
super(context);
this.context = context;
load = new Load(context);
p = new Paint();
Thread t = new Thread(this);
t.setPriority(Thread.MIN_PRIORITY);
t.start();
preTime = System.currentTimeMillis()/1000;
addVariavel("variavel1", 0); //TESTE
addVariavel("valor3", 0); //TESTE
addVariavel("valor4", 0); //TESTE
addVariavel("valor5", 0); //TESTE
addVariavel("valor6", 0); //TESTE
addVariavel("valor7", 0); //TESTE
addVariavel("valor8", 0); //TESTE
}
void init(){
while (getWidth() == 0){ }//aguarndando getWidth
lar = getWidth();
alt = getHeight();
player = new Player();
fase = new Tutorial_001(context);//new Fase1(context);
hud = new HUD();
pt = new PrintText();
menu = new Menu(context);
cam = new Camera(getWidth(), getHeight(), player);
layerImg2 = Bitmap.createBitmap(lar, alt, Bitmap.Config.ARGB_8888);
layer2 = new Canvas(layerImg2);
laterCanvas = new LaterCanvas(context);
setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//int action = event.getActionMasked();
TextKeyBoard.handleTouch(v, event);
NumericKeyBoard.handleTouch(v, event);
hud.handleTouch(v, event);
menu.handleTouch(v, event);
cam.handleTouch(v, event);
handleEventsTouchs.handleTouch(v, event); //SEGURA TODOS OS EVENTOS DE OBJETOS CRIADOS POSTEIORMENTE
return true;
}
});
}
#Override
public void run() {
init();
while (running){
try {
Thread.sleep(INTERVAL);
}catch (Exception e){
Toast t = Toast.makeText(getContext(), "Game Loop:"+e.getMessage(), Toast.LENGTH_LONG);
t.show();
Log.e("GameLogic", "GameLoop erro Inesperado: "+e.getMessage());
}
update();
}
}
public void update(){
//dispara o metodo de desenho
postInvalidate();
timeInGame = (int)((System.currentTimeMillis()/1000)-preTime); //TEMPO JOGADO EM SEGUNDOS
}
public void draw(Canvas c){
super.draw(c);
if(c!=null && player != null && hud != null && menu != null && cam!=null) {
c.drawColor(Color.BLACK);
if(isPlaying) {
cam.draw(c, p);
hud.draw(c, p);
pt.draw(c, p);
if(scoreScreen != null) {
scoreScreen.draw(c);
}
laterCanvas.drawBeforeMsgs(c); //DESENHA TODOS OS DESENHOS SOBREPOSTOS NA MESMA CANVAS.
if(spawnMsgs.size() > 0){
spawnMsgs.get(0).draw(c, p);
}
laterCanvas.drawAfterMsgs(c); //DESENHA TODOS OS DESENHOS SOBREPOSTOS NA MESMA CANVAS.
//c.drawBitmap(layerImg2,0,0,p);
}else {
menu.draw(c, p);
}
}
}
public void relase(){
running = false;
}
public static void addVariavel(String variavelNome, float variavelValor){
if(variaveisName.contains(variavelNome) == false) {
variaveisName.add(variavelNome);
variaveisValue.add(variavelValor);
}
}
public static void removeVariavel(int index){
variaveisName.remove(index);
variaveisValue.remove(index);
}
public static void setVariavelValor(int valiavelIndex, float variavelValor){
variaveisValue.set(valiavelIndex, variavelValor);
}
}